最近在做unity shader forge和marmoset的優化,TA那邊遇到了一個陰影顯示的問題,具體如下:
在Forward Rendering狀態下,靜態場景使用了是shader forge生成的blendlayer類的shader,使用lightmap烘培貼圖后,動態角色走到靜態物體附近時,方向光在角色上的投影,無法投射到使用shader forge材質的物體上,但其他材質和使用marmoset的材質可以正常接收。查詢了一些網站解決方案后,最終確定是custom shader 寫法的問題,shader forge生成的shader屬於自己實現vs和ps的功能,和寫surface shader不同,除了着色用的shader pass("LightMode" = "ForwardBase")外需要Cast shadow用的第2個pass {"LightMode" = "ForwardAdd"}。
下面是一個參考方案的代碼
-
1 "Test" { 2 SubShader { 3 Tags { "RenderType" = "Opaque"} 4 5 // This pass acts the same as the surface shader first pass. 6 // Calculates per-pixel the most important directional light with shadows, 7 // then per-vertex the next 4 most important lights, 8 // then per-vertex spherical harmionics the rest of the lights, 9 // and the ambient light value. 10 11 Pass { 12 Tags {"LightMode" = "ForwardBase"} 13 CGPROGRAM 14 15 #pragma multi_compile_fwdbase 16 #pragma vertex vert 17 #pragma fragment frag 18 #pragma fragmentoption ARB_precision_hint_fastest 19 #include "UnityCG.cginc" 20 #include "AutoLight.cginc" 21 22 struct Input 23 { 24 float4 pos : SV_POSITION; 25 float3 vlight : COLOR; 26 float3 lightDir : TEXCOORD1; 27 float3 vNormal : TEXCOORD2; 28 LIGHTING_COORDS(3,4) 29 }; 30 31 Input vert(appdata_full v) 32 { 33 Input o; 34 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 35 // Calc normal and light dir. 36 o.lightDir = normalize(ObjSpaceLightDir(v.vertex)); 37 o.vNormal = normalize(v.normal).xyz; 38 39 // Calc spherical harmonics and vertex lights. Ripped from compiled surface shader 40 float3 worldPos = mul(_Object2World, v.vertex).xyz; 41 float3 worldNormal = mul((float3x3)_Object2World, SCALED_NORMAL); 42 o.vlight = float3(0); 43 #ifdef LIGHTMAP_OFF 44 45 float3 shlight = ShadeSH9(float4(worldNormal, 1.0)); 46 o.vlight = shlight; 47 #ifdef VERTEXLIGHT_ON 48 o.vlight += Shade4PointLights ( 49 50 unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, 51 52 unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, 53 54 unity_4LightAtten0, worldPos, worldNormal 55 56 ); 57 58 #endif // VERTEXLIGHT_ON 59 #endif // LIGHTMAP_OFF 60 TRANSFER_VERTEX_TO_FRAGMENT(o); 61 return o; 62 } 63 64 float4 _LightColor0; // Contains the light color for this pass. 65 66 half4 frag(Input IN) : COLOR 67 { 68 IN.lightDir = normalize ( IN.lightDir ); 69 IN.vNormal = normalize ( IN.vNormal ); 70 71 float atten = LIGHT_ATTENUATION(IN); 72 float3 color; 73 float NdotL = saturate( dot (IN.vNormal, IN.lightDir )); 74 color = UNITY_LIGHTMODEL_AMBIENT.rgb * 2; 75 76 color += IN.vlight; 77 78 color += _LightColor0.rgb * NdotL * ( atten * 2); 79 return half4(color, 1.0f); 80 } 81 82 ENDCG 83 } 84 85 // Take this pass out if you don't want extra per-pixel lights. 86 // Just set the other lights' Render Mode to "Not Important", 87 // and they will be calculated as Spherical Harmonics or Vertex Lights in the above pass instead. 88 89 Pass { 90 91 Tags {"LightMode" = "ForwardAdd"} 92 93 CGPROGRAM 94 95 #pragma multi_compile_fwdadd 96 #pragma vertex vert 97 #pragma fragment frag 98 #pragma fragmentoption ARB_precision_hint_fastest 99 #include "UnityCG.cginc" 100 #include "AutoLight.cginc" 101 102 struct Input 103 { 104 float4 pos : SV_POSITION; 105 float3 lightDir : TEXCOORD1; 106 float3 vNormal : TEXCOORD2; 107 }; 108 109 Input vert(appdata_full v) 110 { 111 Input o; 112 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 113 114 // Calc normal and light dir. 115 o.lightDir = normalize(ObjSpaceLightDir(v.vertex)); 116 o.vNormal = normalize(v.normal).xyz; 117 118 // Don't need any ambient or vertex lights in here as this is just an additive pass for each extra light. 119 // Shadows won't work here, either. 120 return o; 121 } 122 123 float4 _LightColor0; // Contains the light color for this pass. 124 125 half4 frag(Input IN) : COLOR 126 { 127 IN.lightDir = normalize ( IN.lightDir ); 128 IN.vNormal = normalize ( IN.vNormal ); 129 130 float3 color; 131 float NdotL = saturate( dot (IN.vNormal, IN.lightDir )); 132 color = _LightColor0.rgb * NdotL; 133 return half4(color, 1.0f); 134 } 135 136 ENDCG 137 138 } 139 } 140 141 FallBack "Diffuse" 142 143 }
而使用surface shader時候,只需要在聲明surface時,設置制定的option(fullforwardshadows)就可以了
#pragma surface MarmosetSurfMarmosetDirect vertex:MarmosetVert fullforwardshadows
回過頭來說shader forge插件,場景左側使用sf生成的shader,use lightmap后,實時陰影果斷看不到了
左邊木板為surface shader,右邊為VS+PS Shader,陰影無法投射到石頭上
將shader forge設置為multi-light,他會自動生成
ForwardAdd的pass,這樣陰影就顯示出來了。但照明效果也完全變了樣子
這個問題,在shader forge的論壇上也有提過,作者也說修改過這個bug了,但實際上最新的0.36還是沒有完全解決
如果設置
ForwardAdd后還沒有投影,可以把light的lightmapping 設置為realtimeOnly。或者是再復制一個投影的主光源。
結論,Shader forge還是慎用吧,而且shader node也遠沒有比sky shop這類 uber shader要節省,為了投影,導致使用lightmap和real time的效果差別很大,
完全是得不償失的。



