Rendering Optimization
Terrain Shadow Optimization
A real-time shadow optimization scheme for an SNS community game. The approach uses light-space render textures, baked directional shadow channels, shader-side sampling, and batching-friendly UV data to reduce dynamic shadow cost while preserving readable building shadows.
Overview
The system was designed for an SNS community scene where buildings, decorative elements, and terrain need convincing shadows without the full runtime cost of general dynamic shadows. The solution separates terrain projection shadows, element directional shadows, and batching data so the scene can remain efficient at runtime.
Case 1
Terrain Shadow RenderTexture
A camera is placed at the light position to render a shadow texture from light space. The terrain shader projects this render texture into world space and blends the sampled shadow with the base terrain lighting.
// Project RenderTexture in world position.
fixed4 frag(v2f i) : COLOR
{
fixed4 tex = tex2D(_MainTex, i.texcoord);
fixed spec = tex.a;
float2 shadowUV = mul(_ShadowVPMatrix, i.worldPos).xy * 0.5 + 0.5;
fixed atten = tex2D(_CurrentShadowmap, shadowUV).r;
fixed4 shadowColor = lerp(_ShadowCol, fixed4(1, 1, 1, 1), atten);
tex = _MainColor * _DirectionalLightColor * shadowColor * tex;
half3 normalDir = normalize(UnpackNormal(tex2D(_BumpMap, i.texcoord)));
half3 viewDir = normalize(i.viewDir);
half3 lightDir = normalize(i.lightDir);
half3 halfDir = normalize(viewDir + lightDir);
float specular = max(0, dot(halfDir, normalDir));
tex += _SpecularCol * spec * pow(specular, 48);
return tex;
}
Case 2
Element Shadow Channels
For decorative elements, four directional shadows are baked into separate texture channels. At runtime, the shader samples the RGBA shadow texture and combines it with a direction selector to shade the element efficiently.
fixed4 frag(v2f i) : COLOR
{
fixed4 tex = tex2D(_MainTex, i.texcoord.xy);
fixed4 lm = tex2D(_ShadowTex, i.texcoord.zw);
fixed shadowValue = dot(lm, _ShadowDir);
tex = lerp(tex * shadowValue * _ShadowCol, tex, shadowValue);
tex = lerp(tex, tex * _LightPower, shadowValue * 1.8);
#ifdef _GRADIENT_ON
tex *= i.gradient;
#endif
return tex;
}
Batching
UV3 Shadow Data and Static Batch
To keep scene rendering batching-friendly, extra UV data is generated in UV3 to carry shadow channel information. This allows decorative elements to participate in static batching while still sampling the correct shadow data in the shader.