Shader-平面投影阴影
若阴影只需要落在一块平坦地面上,可以沿光线方向把模型投影到平面,再以阴影颜色绘制。这种方法不必生成 ShadowMap,但它的接收几何也因此受到限制。
平面阴影适合舞台地面、轻量角色展示和某些风格化画面。先理解射线与平面的交点,再讨论 Stencil、透明和退化条件,比只把模型的 y 坐标压成零更稳妥。
1. 从射线和平面求交
设接收平面为 dot(n,q)+d=0,n 为单位法线。模型顶点为 p,阴影传播方向 L 沿光线从物体指向地面:
1 | |
代回即可验证 q 落在平面上。若 L 与平面近乎平行,分母接近零,投影会被放大到很远;需要拒绝这种情况或限制使用范围。
2. 水平地面的特例
当地面为 y=0,公式变为:
1 | |
这段只展示在合法分母下的交点。很多引擎的 LightDir 指向光源,恰好与这里的传播方向相反。虽然同一条无限直线可能求到同一交点,t 的正负筛选仍会改变含义,因此必须写清方向定义。
点光源可从光源位置 S 沿 p-S 发射射线求交。这里的 t 从光源起算,若要保证地面位于物体之后,还要检查交点相对光源和物体的位置,不能照搬方向光的筛选条件。

原笔记配图:点光源、物体高度与接收平面的相似三角形关系。与平行光模型不同,投影方向随顶点位置改变。
3. 从公式到绘制
顶点 Shader 先将模型顶点变到世界空间,投影到接收平面,再按主相机 VP 输出。阴影 Pass 使用专门颜色和混合状态,不改变正常模型的几何数据。
地面可以正常写深度,阴影通过深度测试贴在它上面,并用适当偏移减少 Z-fighting。任意固定偏移都可能在远处或掠射角露馅,应结合深度精度测试。
阴影颜色是美术控制的遮暗近似,不是物理上完整的光传输。若场景已经有实时阴影,还需避免重复叠加。

原笔记配图:旧项目中的角色和平面投影阴影,接收者仍是一块平面。
4. 为什么会叠黑
投影模型的多个三角形可能在同一像素重叠。如果每个三角形都进行 Alpha 混合,同一角色的阴影可能被重复压暗。
可以在适当的 Stencil 范围内记录已绘制区域,让同一次投影只影响一次;也可以先渲染遮罩再统一合成。Stencil 位要和管线其他功能协调,不能随意清掉整个模板缓冲。
多个角色是否允许阴影叠加,是另一个设计选择。不能把“避免单个网格自重叠”和“所有阴影永远不叠加”当作同一个状态配置。
5. 遮罩与裁剪
只有某块地板接收时,可使用地板模板值或接收范围 Mask。否则投影几何可能穿过桌面、墙壁和台阶,形成明显的悬浮片。
树叶、头发等 Alpha Clip 材质还要保留必要的覆盖判断,否则阴影会变成整张贴片。投影的背面剔除设置也需要测试,因为压扁后的绕序可能与原模型观察状态不同。
6. 能力边界
方法假定一个接收平面,不会自动沿起伏地形弯曲,也无法正确表达多层遮挡、接触关系与距离相关软阴影。模糊投影遮罩可以增加柔和感,但不能修复错误的接收几何。
当这些限制开始主导画面,就应考虑 ShadowMap、屏幕空间接触阴影或其他方案,而不是继续给平面投影加越来越多特例。
总结
平面阴影是受约束的几何投影。求交公式、光向定义和退化处理决定位置是否正确;深度、Stencil 与接收范围决定合成是否稳定。明确它的限制,才能发挥它成本低、容易控制的优势。