【URP】[平面阴影]原理与实现

【从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

    cpp 复制代码
    Shader "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开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

相关推荐
xcLeigh15 小时前
Unity基础:Instantiate实例化——在运行时动态创建游戏物体
游戏·unity·游戏引擎·教程·instantiate
流浪打工人1 天前
Unity3d TimeLine中audio track的使用
unity
淡海水2 天前
02-07-原理篇-GC暂停时间的本质
开发语言·unity·c#·gc·暂停时间
WarPigs2 天前
UI路径配置SO>字典json工作流
ui·unity
Martina_03212 天前
AI生成的海岸城市一移动镜头,水面反射就跳?用6步统一反射探针、SSR与平面反射
图像处理·人工智能·平面·3d·ai·自然语言处理·游戏策划
淡海水2 天前
02-06-原理篇-增量式GC与并发GC
算法·unity·c#·并发·gc·增量
阿松爱学习2 天前
【Unity开发】Aspose.PDF的使用
unity·pdf·c#
淡海水2 天前
02-04-原理篇-GC Roots与对象图遍历
算法·unity·c#·图·gc·遍历·roots
谢亮_vipxieliang3 天前
Kubernetes 的“操作系统化”:从容器编排到 AI 基础设施控制平面
人工智能·平面·kubernetes
qq_284274053 天前
机械原理笔记:平面机构自由度计算(复合铰链、局部自由度、虚约束)与四杆机构入门(含考点)
笔记·学习·平面·自动化·制造