Unity投射陰影--V2F_SHADOW_CASTER,TRANSFER_SHADOW_CASTER_NORMALOFFSET(o),SHADOW_CASTER_FRAGMENT(i)三部分
阿新 • • 發佈:2020-12-23
技術標籤:Unity Shader
作用:讓物體能夠投射陰影
原理:返回裁剪空間的座標,寫入depth buff,讓攝像機在光源空間渲染出深度圖,再進一步渲染出螢幕陰影紋理(某些平臺並沒有這一步驟https://blog.csdn.net/zengjunjie59/article/details/111356636),最後在光照pass裡面對紋理進行取樣。
例子:
Pass { Tags { "LightMode" = "ShadowCaster" } CGPROGRAM #pragma target 3.0 // 定義相關key,具體看https://blog.csdn.net/zengjunjie59/article/details/111404824 #pragma multi_compile_shadowcaster #include "CustomShadows.cginc" #pragma vertex MyShadowVertexProgram #pragma fragment MyShadowFragmentProgram ENDCG }
#if !defined(CUSTOM_SHADOWS_INCLUDE) #define CUSTOM_SHADOWS_INCLUDE #include "UnityCG.cginc" //如果直接呼叫unity自帶的巨集,則可以這樣寫 struct Interpolators{ V2F_SHADOW_CASTER; }; Interpolators MyShadowVertexProgram(appdata_base v){ Interpolators i; TRANSFER_SHADOW_CASTER_NORMALOFFSET(i); return i; } float4 MyShadowFragmentProgram(Interpolators i): SV_TARGET { SHADOW_CASTER_FRAGMENT(i); } #endif
首先看第一個巨集:V2F_SHADOW_CASTER
原始碼:
// On D3D reading screen space coordinates from fragment shader requires SM3.0 #define UNITY_POSITION(pos) float4 pos : SV_POSITION #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX) // Rendering into point light (cubemap) shadows #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0; #else // Rendering into directional or spot light shadows #define V2F_SHADOW_CASTER_NOPOS // Let embedding code know that V2F_SHADOW_CASTER_NOPOS is empty; so that it can workaround // empty structs that could possibly be produced. #define V2F_SHADOW_CASTER_NOPOS_IS_EMPTY #endif // Declare all data needed for shadow caster pass output (any shadow directions/depths/distances as needed), // plus clip space position. #define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS UNITY_POSITION(pos)
其實就相當於:
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
float4 pos : SV_POSITION // 用於儲存裁剪座標
float3 vec : TEXCOORD0 // 用於儲存光源到頂點的世界空間方向向量
#else
float4 pos : SV_POSITION
#endif
頂點著色器裡用的巨集:TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
原始碼:
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
// 計算世界空間光源到頂點的向量
// 計算頂點裁剪座標
#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
#else
// 轉換到才裁剪空間齊次座標,並執行頂點法向偏差
// 便宜裁剪齊次座標的z值,並返回頂點裁剪座標點
#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
opos = UnityApplyLinearShadowBias(opos);
#endif
// Vertex shader part, with support for normal offset shadows. Requires
// position and normal to be present in the vertex input.
#define TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)
片元著色器裡用的巨集:SHADOW_CASTER_FRAGMENT(i)
原始碼:
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
// _LightPositionRange.w是1/Range, Range是光照範圍
// UnityEncodeCubeShadowDepth是如果檢測到不能用浮點數則把float值的不同位存到rgba各個分量中
// 整段算的就是光源空間下頂點的深度,值域[0, 1], 期間也用了z值偏差(+unity_LightShadowBias.x)
#define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
#else
#define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif
最後:
有個小疑問:點光源,定義SHADOWS_CUBE之後,在SHADOW_CASTER_FRAGMENT巨集裡的第一個if分支下直接返回0效果並沒有什麼區別,猜測是某些平臺會有用。有知道的人,還望指教指教