UNITYシェーダ研究 Lambertモデル実装
前回は 新規で作ったUNITシェーダの説明したので
簡単なシェーダ作ります
コードはココ
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_3
基本シェーダ(Albedo)
先ほどのシェーダからFOGを削除しシンプルにします
テクスチャは・・・雰囲気欲しいからつけておく
Shader "Unlit/yUnit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); return col; } ENDCG } } }
テクスチャ(Albedo)のみですね
Lambert
最も簡単なライティングモデルの ランバート反射を作ります
先ほどのAlbedoはライトの影響をうけなかったけど今度はうけます
ランバート反射は、面にライトが当たる角度により影響の強さが変わる
具体的には 法線とライトベクトルの内積を乗算する
Shader "Unlit/yUnit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags{ "LightMode" = "ForwardBase" } Cull Back CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.normal = UnityObjectToWorldNormal(v.normal); return o; } float3 frag (v2f i) : SV_Target { // sample the texture float4 col = tex2D(_MainTex, i.uv); // Diffuse float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float3 NdotL = dot( i.normal, lightDir); float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; return diffuse; } ENDCG } } }
LightMode
Tags{ "LightMode" = "ForwardBase" }
これを付けないと安定しなかったので付けた
LightModeにより呼ばれるパスが変わる
ForwardBaseは Forwardレンダリングのメインのライトのパス
ForwardAddは Forwardレンダリングのメイン以外のライトがそれぞれ呼ばれる
Deferredは Deferredレンダリングのパス
ShadowCasterは 影のパス
など
構造体
struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; };
法線が必要なので NORMALが追加
シェーダ中身
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.normal = UnityObjectToWorldNormal(v.normal); return o; } float3 frag (v2f i) : SV_Target { // sample the texture float4 col = tex2D(_MainTex, i.uv); // Diffuse float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float3 NdotL = dot( i.normal, lightDir); float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; return diffuse; }
o.normal = UnityObjectToWorldNormal(v.normal);
法線がローカル座標系なので World座標系に変換する
WorldSpaceLightPos0、LightColor0 はビルトイン定数(Lighting.cginc のinclude必要)
WorldSpaceLightPos0: 0番目のライトのポジション(ディレクショナルライトは方向)
LightColor0: 0番目のライトの色
float3 NdotL = dot( i.normal, lightDir);
ここで 法線とライトの内積を計算する
ディフューズは ライトと法線の内積に ライトカラーとテクスチャカラーを掛ければ求められる
内積は負の場合(裏面)があるので その場合は0にしておく
ライトを動かせば ライティングも変わるようになった
でも少し暗い
またSphereを置けばわかりやすいが、陰のつき方が90度でわかれている
実際のオブジェクトは光の回析や吸収反射などで もっとゆるやかに出るべきである
ハーフランバート
先ほどのランバートだと、ライティングが90度で終わる
もう少しゆるやかにしたいのでハーフランバートを適用してみる 先ほどのランバート反射では ライトと法線の内積が -1~1 であったが 0~1の範囲にリマップすれば ゆるやかになる
// float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz;
// float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; // float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz; float3 diffuse = pow(NdotL*0.5 + 0.5, 2) * _LightColor0.rgb * col.xyz;
他にも float3 diffuse = (NdotL*0.75 + 0.25) * _LightColor0.rgb * col.xyz;
とか色々と考えれるので、好みで使い分けよう