【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开始探索游戏渲染】专栏-直达

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

相关推荐
迪普阳光开朗很健康1 天前
UnityScrcpy 可以让你在unity面板里玩手机的插件
unity·游戏引擎
zl_vslam2 天前
SLAM中的非线性优-3D图优化之地平面约束(十四)
算法·计算机视觉·平面·3d
陈言必行2 天前
Unity 之 设备性能分级与游戏画质设置与设备自动适配指南
游戏·unity·游戏引擎
CreasyChan2 天前
Unity DOTS技术栈详解
unity·c#·游戏引擎
在路上看风景2 天前
1.1 Unity资源生命周期管理与内存机制
unity
CreasyChan2 天前
Unity的ECS(Entity Component System)架构详解
unity·架构·游戏引擎
神码编程2 天前
【Unity】 HTFramework框架(六十九)Log在编辑器日志中自定义点击事件
unity·编辑器·游戏引擎
TO_ZRG3 天前
Unity打包安卓、iOS知识点
android·unity·android studio
冰凌糕3 天前
Unity3D Shader 顶点动画 呼吸和水波
unity
呆呆敲代码的小Y3 天前
【Unity 实用工具篇】| UX Tool 工具 快速上手使用,提高日常开发效率
游戏·unity·游戏引擎·游戏程序·ux