关于Unity动态物体无法向使用使用custom shader和lightmap的物体投射阴影
最近在做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的效果差别很大,
完全是得不偿失的。