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

【从UnityURP开始探索游戏渲染】专栏-直达

实现原理

光源点L0,光源方向L,光源距离要投影的的平面距离d;要投影的平面的向上法线向量TerrainNormal,平面上的任意点TerrainPos;现在要计算出光源投向空间中模型上的点在平面上的平面投影点坐标P。

  • 示意图:

计算公式

  • 平面上任意向量与该平面法向量点乘为0: (p-TerrainPos)·TerrainNormal=0
  • 平面映射的P点是由光射到顶点延伸得到的: p=d\*L+L0
  • 由上述两个公式联立方程,带入求解d:
  • 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开始探索游戏渲染】专栏-直达

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

相关推荐
June bug3 小时前
【领域知识】休闲游戏一次发版全流程:Google Play + Apple App Store
unity
星夜泊客6 小时前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
dzj20216 小时前
PointerEnter、PointerExit、PointerDown、PointerUp——鼠标点击物体,则开始旋转,鼠标离开或者松开物体,则停止旋转
unity·pointerdown·pointerup
心前阳光7 小时前
Unity 模拟父子关系
android·unity·游戏引擎
在路上看风景10 小时前
26. Mipmap
unity
咸鱼永不翻身12 小时前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景12 小时前
4.2 OverDraw
unity
在路上看风景13 小时前
1.10 CDN缓存
unity
ellis19701 天前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix1 天前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎