【从UnityURP开始探索游戏渲染】专栏-直达
实现原理
光源点L0,光源方向L,光源距离要投影的的平面距离d;要投影的平面的向上法线向量TerrainNormal,平面上的任意点TerrainPos;现在要计算出光源投向空间中模型上的点在平面上的平面投影点坐标P。

计算公式
- 平面上任意向量与该平面法向量点乘为0: (p−TerrainPos)⋅TerrainNormal=0(p−TerrainPos)⋅TerrainNormal=0
- 平面映射的P点是由光射到顶点延伸得到的: p=d∗L+L0p=d∗L+L0
- 由上述两个公式联立方程,带入求解d:
- d=((TerrainPos−L0)⋅TerrainNormal)/(L⋅TerrainNormal)d=((TerrainPos−L0)⋅TerrainNormal)/(L⋅TerrainNormal)
示例
实现要点说明:
- 使用URP核心库(ShaderLibrary/Core.hlsl)确保兼容性
- 通过_CBUFFER_START声明材质属性
- 顶点着色器中实现完整投影公式计算
- 添加透明度衰减效果增强真实感
- 使用Transparent渲染队列和混合模式
- 禁用ZWrite避免深度冲突
使用方式:
-
创建材质并指定该Shader
-
通过C#脚本设置_LightPos等参数
-
将材质应用到需要投射阴影的物体上
-
确保接收阴影的平面有适当渲染材质
-
PlaneShadow.shader
cppShader "Custom/PlaneShadow" { Properties { _ShadowColor("Shadow Color", Color) = (0,0,0,0.5) } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; }; struct Varyings { float4 positionCS : SV_POSITION; float alpha : TEXCOORD0; }; CBUFFER_START(UnityPerMaterial) float4 _ShadowColor; CBUFFER_END // 外部传入参数 float3 _LightPos; // 光源位置L0 float3 _LightDir; // 光源方向L(需归一化) float3 _TerrainPos; // 平面上任意点 float3 _TerrainNormal; // 平面法线(需归一化) Varyings vert(Attributes IN) { Varyings OUT; // 计算投影参数d float d = dot((_TerrainPos - _LightPos), _TerrainNormal) / dot(_LightDir, _TerrainNormal); // 计算模型顶点到光源的向量 float3 lightToVertex = IN.positionOS.xyz - _LightPos; // 计算投影点P = L0 + (d / (L·(V-L0))) * (V-L0) float t = d / dot(_LightDir, lightToVertex); float3 shadowPos = _LightPos + t * lightToVertex; // 转换到裁剪空间 OUT.positionCS = TransformWorldToHClip(shadowPos); // 根据距离计算透明度衰减 OUT.alpha = saturate(1.0 - length(shadowPos - IN.positionOS.xyz) * 0.1); return OUT; } half4 frag(Varyings IN) : SV_Target { half4 col = _ShadowColor; col.a *= IN.alpha; return col; } ENDHLSL } } }
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)