用頂點Shader實現的實時陰影
如圖所見,壓扁的模型而已
看到大概都知道該怎麼做了,新加一個PASS並做頂點變換,新頂點為世界坐標系下光照方向射線過原頂點與地平面的交點,下面的是完整Shader文件
Shader "Unlit/MeshShadow"{ Properties { _MainTex ("Texture", 2D) = "white" {} _ShadowAlpha ("Shadow Alpha", Range(0,1)) = 0.5 _LightDir ("Light Dir", Vector) = (1,1,1) _GroundY ("GroundY", float) = 0 } SubShader { Pass { Tags { "RenderType"="Opaque" } CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) 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); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } Pass { Tags{ "RenderType"="Transparent" "Queue"="Transparent" } Stencil { Ref 1 Comp NotEqual Pass Replace ReadMask 1 WriteMask 1 } Blend SrcAlpha OneMinusSrcAlpha ZWrite OFF CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; half _ShadowAlpha; half3 _LightDir; half _GroundY; v2f vert (appdata v) { v2f o; half4 worldPos = mul(unity_ObjectToWorld, v.vertex); float d = (_GroundY - worldPos.y) / _LightDir.y; worldPos.xyz += d * _LightDir; o.vertex = mul(UNITY_MATRIX_VP,worldPos); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = fixed4(0,0,0,_ShadowAlpha); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } }}
關鍵代碼
v2f vert (appdata v){ v2f o; half4 worldPos = mul(unity_ObjectToWorld, v.vertex); float d = (_GroundY - worldPos.y) / _LightDir.y; worldPos.xyz += d * _LightDir; o.vertex = mul(UNITY_MATRIX_VP,worldPos); UNITY_TRANSFER_FOG(o,o.vertex); return o;}
需要專門提一下的是半透的處理,模型半透時難免會這樣
深度緩衝做過濾是沒用的,精度不夠,所以最後用的Stencil解決
Stencil{ Ref 1 //目標值設為1 Comp NotEqual //比較緩衝是否與1相等,相等說明已經繪製過了,不能再繪製 Pass Replace //如果成功繪製,則寫緩衝為1 ReadMask 1 //設定Mask只修改第一位,可做相應修改。這是為了避免和其他Stencil衝突,沒衝突可以刪掉 WriteMask 1}
至於缺點?
無法拐彎,會被起伏的地面遮擋。
另外實際使用的時候請注意CULL的問題,模型的Bounds需要擴大。
想不被地面起伏遮擋也是可以的。影子部分ZTest OFF,然後在繪製人物部分加上
Stencil{ Ref 1 Comp Always Pass Replace ReadMask 1 WriteMask 1}
保證影子絕對不遮擋人物就可以
但是就會出現這種情況
本來就是一個劣化方案,根據需要選擇把。
推薦閱讀:
※讓角色半透明:後期模糊(二)
※【GPU精粹與Shader編程】(一) 開篇 & 全系列11本書核心知識點總覽
※走樣與反走樣(Aliasing/Anti-Aliasing):Graphics Cases
※UE4中快速實現簡單GPU流體模擬