【URP】[实时阴影]计算流程解析
在Unity URP中,实时阴影的工作流程基于ShadowMap技术实现,主要流程如下:【从UnityURP开始探索游戏渲染】专栏-直达
一、阴影生成流程
阴影贴图(ShadowMap)生成
在光源位置设置虚拟相机,渲染场景深度到纹理:
[*]主光源阴影由ShadowCaster Pass处理,输出深度图到_MainLightShadowmapTexture
[*]使用级联阴影(Cascaded Shadow Mapping)提升精度:将ShadowMap划分为2×2图集,对应不同精度等级
csharp
// URP核心流程if (主光源开启阴影) {
MainLightShadowCasterPass.Render();// 生成_MainLightShadowmapTexture
}深度值比较
正常渲染时执行:
[*]将像素坐标转换到光源空间获取深度值
[*]采样ShadowMap比较深度:若当前深度 > ShadowMap值,则判定为阴影区域
二、关键Pass的作用
ShadowCaster Pass
[*]作用:专用于生成阴影贴图
[*]输出目标:_MainLightShadowmapTexture(主光源)或自定义ShadowMap
[*]Shader要求:
hlsl
Pass {
Tags { "LightMode" = "ShadowCaster" }
#pragma multi_compile_shadowcaster // 生成SHADOWS_DEPTH/SHADOW_CUBE宏
V2F_SHADOW_CASTER; // 声明数据结构
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) // 顶点着色器处理深度偏移
}来自于Lit的阴影投射pass
// ShadowCaster 计算灯光的深度贴图,相当于以光的位置主动投射到物体上,形成的偏移阴影。用来投射阴影到其他位置的计算。
// 这里涉及到_ShadowBias在Shadow.hlsl中x分量表示DepthBias深度方向偏移,y分量表示NormalBias法线方向偏移。
Pass
{
Name "ShadowCaster"
Tags
{
"LightMode" = "ShadowCaster"
}
// -------------------------------------
// Render State Commands
ZWrite On
ZTest LEqual
ColorMask 0
Cull
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Universal Pipeline keywords
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}ShadowCasterPass.hlsl
阴影投射主要计算在顶点计算位置,片元不需要处理纹理颜色阴影返回0给黑色。
#ifndef UNIVERSAL_SHADOW_CASTER_PASS_INCLUDED
#define UNIVERSAL_SHADOW_CASTER_PASS_INCLUDED
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
#if defined(LOD_FADE_CROSSFADE)
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
#endif
// Shadow Casting Light geometric parameters. These variables are used when applying the shadow Normal Bias and are set by UnityEngine.Rendering.Universal.ShadowUtils.SetupShadowCasterConstantBuffer in com.unity.render-pipelines.universal/Runtime/ShadowUtils.cs
// For Directional lights, _LightDirection is used when applying shadow Normal Bias.
// For Spot lights and Point lights, _LightPosition is used to compute the actual light direction because it is different at each shadow caster geometry vertex.
float3 _LightDirection;
float3 _LightPosition;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
#if defined(_ALPHATEST_ON)
float2 uv : TEXCOORD0;
#endif
float4 positionCS : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 GetShadowPositionHClip(Attributes input)
{
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
return positionCS;
}
Varyings ShadowPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
#if defined(_ALPHATEST_ON)
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
#endif
output.positionCS = GetShadowPositionHClip(input);
return output;
}
half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
UNITY_SETUP_INSTANCE_ID(input);
#if defined(_ALPHATEST_ON)
Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
#endif
#if defined(LOD_FADE_CROSSFADE)
LODFadeCrossFade(input.positionCS);
#endif
return 0;
}
#endifDepthOnly Pass
[*]作用:生成场景深度图(非直接用于阴影)
[*]输出目标:_CameraDepthTexture
[*]阴影关联:为屏幕空间阴影计算提供场景深度数据,非阴影贴图直接来源
三、阴影生成位置
[*]核心Pass:阴影完全由LightMode="ShadowCaster"的Pass生成
[*]执行阶段:在MainLightShadowCasterPass渲染管线阶段完成
[*]纹理存储:
[*]主光源阴影 → _MainLightShadowmapTexture
[*]点光源阴影 → CubeMap形式存储
四、深度偏移处理
通过TRANSFER_SHADOW_CASTER_NORMALOFFSET宏:
[*]自动计算法线偏移
[*]解决Shadow Acne(阴影痤疮)和Peter Panning(边缘剥离)问题
五、级联阴影优化
URP采用2×2级联图集:
[*]每级对应不同视锥区域
[*]动态分配精度:近距离高精度,远距离低精度
[*]通过GetShadowCasterBounds()计算光源影响范围
⚠️ 注意事项
[*]物体需包含ShadowCaster Pass才能投射阴影
[*]深度测试冲突可能导致阴影缺失,需检查材质配置
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]