Unityシェーダーチュートリアル
ガラスの表現手法いろいろ

シェーダー

Unity シェーダー ガラス

こんなステージで作っていきたいと思います。

完全なBaked Lightmap、リアルタイムライトは無し。

 

 反射

ガラスに反射は必須なので Reflection Probe で反射を入れることになりますが、

よりガラスっぽく見せるためのコツがあります。

それは、反射素材に強烈な光源を入れる事です。

Unity シェーダー ガラス

 

 背面とのブレンド方法を乗算にする

赤いガラスを作ってみたいと思います。まず単純に Surface Shader の色を赤に変更すると。。。

全く変化がありません。

Unity シェーダー ガラス

 

これは、部屋のライティングが理由です。

冒頭で述べましたが、この部屋は完全にライトマップ化されており、

リアルタイムライトが1つもありません。

 

Surface Shader はライトに影響するシェーダーなので、

ライトが無い = 真っ暗 となり、いくら色を変更したところで画に影響が出ないのです。

 

そこでポイントライトを1つ置いてみます。

Unity シェーダー ガラス

ポイントライトで照らされている部分は赤くなりました。。。

ですが、これは求めている画ではありません。

 

学生の頃を思い出してみてください。洗脳の時代です。

英単語を暗記するために赤い透明シートを誰もが使っていたでしょう。

シート越しに景色を見ると、どんな色であっても全て赤くなっていたはずです。

 

色付きガラスは、背景に対してその色が乗算されていなければならないのです。

Unity シェーダー ガラス

まさにあの赤い暗記シートです。

これを実現するためにはシェーダーを Vertex / Fragment で記述する必要があります。

 

Shader "Custom/Shader_Glass01" {
Properties {
_Color ("Color" , Color ) = (1, 1, 1, 1)
_Smoothness ("Smoothness", Range(0, 1)) = 1
_Alpha ("Alpha" , Range(0, 1)) = 0
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
// 背景とのブレンド法を「乗算」に指定
Blend DstColor Zero
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _Alpha;
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(_Color, 0, _Alpha), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
}
ENDCG
}
FallBack "Standard"
}
view raw Shader_Glass_01 hosted with ❤ by GitHub

 

 両面を描画する

 

両面を描画する事によって、厚みがより分かりやすくなります。

内側の反射には色が乗り、強度を調整できるようにします。

Unity シェーダー ガラス

 

Shader "Custom/Shader_Glass02" {
Properties {
_Color ("Color" , Color ) = (1, 1, 1, 1)
_Smoothness ("Smoothness" , Range(0, 1)) = 1
_Alpha ("Alpha" , Range(0, 1)) = 0
_InRefl ("Inner Reflectivity", Range(0, 1)) = 1
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
// 背景とのブレンド法を「乗算」に指定
Blend DstColor Zero
Cull Front
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _Alpha;
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(_Color, 0, _Alpha), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
half _InRefl;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
// 反射強度は o.Occlusion で調整できる
o.Occlusion = _InRefl;
}
ENDCG
Cull Back
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _Alpha;
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(_Color, 0, _Alpha), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
}
ENDCG
}
FallBack "Standard"
}
view raw Shader_Glass_02 hosted with ❤ by GitHub

 

 カット面と角の透明度を下げる

カット面と角の透明度を下げることによって、

さらに厚み感や、ガラスとしての存在感が増します。

 

テクスチャでの描き分けはUV展開の必要があり面倒なので

頂点カラーをマスクとして利用します。以下の記事が参考になります。

 

Unity シェーダー ガラス

Unity シェーダー ガラス

 

Shader "Custom/Shader_Glass03" {
Properties {
_Color ("Color" , Color ) = (1, 1, 1, 1)
_Smoothness ("Smoothness" , Range(0, 1)) = 1
_AlphaF ("Alpha (Face)" , Range(0, 1)) = 0
_AlphaE ("Alpha (Edge)" , Range(0, 1)) = 0
_InRefl ("Inner Reflectivity", Range(0, 1)) = 1
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
// 背景とのブレンド法を「乗算」に指定
Blend DstColor Zero
Cull Front
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _AlphaF;
half _AlphaE;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(lerp(_Color, 0, _AlphaF), lerp(_Color, 0, _AlphaE), i.color), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
half _InRefl;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
// 反射強度は o.Occlusion で調整できる
o.Occlusion = _InRefl;
}
ENDCG
Cull Back
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _AlphaF;
half _AlphaE;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(lerp(_Color, 0, _AlphaF), lerp(_Color, 0, _AlphaE), i.color), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
}
ENDCG
}
FallBack "Standard"
}
view raw Shader_Glass_03 hosted with ❤ by GitHub

 

 側面の透明度を下げる

光が目に届くまでにガラス内を通る距離や、屈折による影響等で、

面がカメラに正対していない箇所が暗くなることがります。

 

物理的に正しい計算をさせるのは無理ですが、

擬似的に表現してあげることで、よりガラスらしくなります。

Unity シェーダー ガラス

 

Shader "Custom/Shader_Glass04" {
Properties {
_Color ("Color" , Color ) = (1, 1, 1, 1)
_Smoothness ("Smoothness" , Range(0, 1)) = 1
_AlphaF ("Alpha (Face)" , Range(0, 1)) = 0
_AlphaE ("Alpha (Edge)" , Range(0, 1)) = 0
_AlphaR ("Alpha (Rim)" , Range(0, 1)) = 0
_InRefl ("Inner Reflectivity", Range(0, 1)) = 1
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
// 背景とのブレンド法を「乗算」に指定
Blend DstColor Zero
Cull Front
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _AlphaF;
half _AlphaE;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(lerp(lerp(_Color, 0, _AlphaF), lerp(_Color, 0, _AlphaE), i.color), 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
half _InRefl;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
// 反射強度は o.Occlusion で調整できる
o.Occlusion = _InRefl;
}
ENDCG
Cull Back
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _AlphaF;
half _AlphaE;
half _AlphaR;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
float3 normal : NORMAL;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
float3 normal : NORMAL;
float3 viewDir : TEXCOORD0;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
o.normal = v.normal;
o.viewDir = normalize(ObjSpaceViewDir(v.vertex));
return o;
}
fixed4 frag (v2f i) : SV_Target {
float rimFallOff = lerp(1, dot(i.viewDir, i.normal), _AlphaR);
return fixed4(lerp(lerp(_Color, 0, _AlphaF), lerp(_Color, 0, _AlphaE), i.color) * rimFallOff, 1);
}
ENDCG
}
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
struct Input {
fixed null;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
}
ENDCG
}
FallBack "Standard"
}
view raw Shader_Glass_04 hosted with ❤ by GitHub

 

 カメラ空間法線による屈折

塊状のガラスは屈折を入れることでよりガラスらしさが出ます。

 

上記記事ではノーマルマップを使って擬似屈折させていますが、

今回はカメラ空間法線を使って擬似屈折させてみます。次はこんなシーンでやっていきます。

Unity シェーダー ガラス

 

カメラ空間法線とは・・・これです。

Unity シェーダー ガラス

面がカメラに正対していれば(R:0.5, G:0.5, B:1.0)となり、

横にズレていれば、R が 0 ~ 1

縦にズレていれば、G が 0 ~ 1

の値をとります。

 

このズレている箇所、つまりカメラに正対していない部分に

擬似屈折を入れることで、平面的な塊ガラスの厚み感を表現します。

角部分はさらに屈折を大きくします。

Unity シェーダー ガラス

Unity シェーダー ガラス

 

Shader "Custom/Shader_Glass05" {
Properties {
_Color ("Color" , Color ) = (1, 1, 1, 1)
_Smoothness ("Smoothness" , Range(0, 1)) = 1
_AlphaF ("Alpha (Face)" , Range(0, 1)) = 0
_AlphaE ("Alpha (Edge)" , Range(0, 1)) = 0
_AlphaR ("Alpha (Rim)" , Range(0, 1)) = 0
_DistortionF ("Distortion (Face)" , Range(0, 1)) = 0
_DistortionE ("Distortion (Edge)" , Range(0, 1)) = 0
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
// 屈折を入れる場合はシェーダー内で乗算させるので「通常の重ね設定」
Blend SrcAlpha OneMinusSrcAlpha
// 背景画をテクスチャとして取得
GrabPass{ "" }
Cull Front
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GrabTexture;
half3 _Color;
half _AlphaF;
half _AlphaE;
half _DistortionF;
half _DistortionE;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
float3 normal : NORMAL;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
float3 VSnormal : TEXCOORD0;
float4 screenPos : TEXCOORD1;
float distance : TEXCOORD2;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
o.VSnormal = COMPUTE_VIEW_NORMAL;
o.screenPos = ComputeScreenPos(o.vertex);
o.distance = distance(v.vertex, _WorldSpaceCameraPos);
return o;
}
fixed4 frag (v2f i) : SV_Target {
// 擬似屈折を入れる
half3 offset = i.VSnormal * (lerp(_DistortionF, _DistortionE, i.color) * (1 / i.distance));
half3 grab = tex2D(_GrabTexture, (i.screenPos.xy / i.screenPos.w) + offset);
return fixed4(grab * lerp(lerp(_Color, 0, _AlphaF), lerp(_Color, 0, _AlphaE), i.color), 1);
}
ENDCG
}
Cull Back
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
half3 _Color;
half _AlphaF;
half _AlphaE;
struct appdata {
float4 vertex : POSITION;
half color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target {
// 透明度だけ描画
// 方向の違いによる厚みに大きな差が無いので側面不透明度はいらない
return fixed4(0, 0, 0, lerp(_AlphaF, _AlphaE, i.color));
}
ENDCG
}
// 擬似屈折で内面が見えないので、表面のみ描画
Cull Back
// V/FシェーダーはReflection Probeに反応しないので
// 反射だけを描画するSurface Shaderを追記する
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard alpha
half _Smoothness;
half _AlphaR;
struct Input {
float3 viewDir;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Smoothness = _Smoothness;
o.Alpha = 0;
}
ENDCG
}
FallBack "Standard"
}
view raw Shader_Glass_05 hosted with ❤ by GitHub

 

物理的には屈折や全反射によって景色が反転して見えたりもするので、

GrabPassで取得した背景を左右反転させてみるのも良いかもしれない。

 

あくまでも擬似的な方法なので実際の塊ガラスの見た目とはかなり違いますね。

ただ、雰囲気としては良いのではないでしょうか。

Show Comments0

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)