You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity URP中2D纹理数组与阴影地图结合的问题排查

解决Unity 6.4 URP中2D纹理数组与阴影结合的Shader问题

问题1:头文件冲突编译错误

同时引入Lighting.hlsl和UnityCG.cginc触发PackHeightmap重定义错误,核心原因是两个头文件重复定义了相同工具函数。

解决方案

URP的HLSL环境无需依赖UnityCG.cginc的UNITY_DECLARE_TEX2DARRAY宏,直接使用URP原生HLSL资源声明方式即可:

  • 用TEXTURE2D_ARRAY(_TerrainLayers);替代UNITY_DECLARE_TEX2DARRAY(_TerrainLayers);
  • 用SAMPLER(sampler_TerrainLayers);声明采样器
  • 用SAMPLE_TEXTURE2D_ARRAY(_TerrainLayers, sampler_TerrainLayers, uv, layerIndex)完成采样

彻底移除UnityCG.cginc引入,仅保留URP的Lighting.hlsl,即可解决冲突。


问题2:旧API无阴影显示

使用Legacy的Lighting.cginc和AutoLight.cginc无法在URP中正常渲染阴影,添加LightweightForward标签后材质变洋红,原因是Legacy光照管线与URP不兼容,LightweightForward为URP早期废弃标签。

解决方案

放弃Legacy API,改用URP原生HLSL写法:

  1. 确保SubShader添加"RenderPipeline" = "UniversalPipeline"标签
  2. 使用URP提供的GetVertexPositionInputs、GetShadowCoord等函数处理阴影坐标
  3. 添加正确的阴影编译宏(如_MAIN_LIGHT_SHADOWS_CASCADE)
  4. 必须添加URP规范的ShadowCaster Pass,否则物体无法投射阴影

问题3:URP HLSL写法出现阴影级联边缘Artifacts

阴影级联边缘出现异常,核心原因是世界空间位置计算错误,且阴影采样未处理级联过渡。

解决方案

关键代码修正点:

  1. 正确计算世界空间位置:原代码中OUT.positionWorld = IN.positionOS;直接使用模型空间坐标,需改为:
    OUT.positionWorld = TransformObjectToWorld(IN.positionOS.xyz);
    
  2. 使用URP内置阴影过渡函数:用MainLightShadowing替代MainLightRealtimeShadow,自动处理级联阴影边缘过渡
  3. 添加软阴影编译宏:增强阴影平滑度,减少边缘锯齿
  4. 添加ShadowCaster Pass:确保物体能正确投射阴影

修正后的完整Shader代码:

Shader "Proc Env/Terrain"
{
    Properties
    {
        _TerrainLayers ("Layers", 2DArray) = "" {}
        _LayerWeights("Weight Map", 2D) = "white" {}
        _UVScale ("UVScale", Float) = 1.0
    }
    SubShader
    {
         Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        // 主渲染Pass
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma require 2darray
            // 阴影相关编译宏
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _SHADOWS_SOFT

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 positionWorld : TEXCOORD1;
                float4 shadowCoords : TEXCOORD2;
                float3 normalWorld : TEXCOORD3;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                // 正确转换裁剪空间坐标
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                // 正确计算世界空间位置
                OUT.positionWorld = TransformObjectToWorld(IN.positionOS.xyz);
                // 转换法线到世界空间
                OUT.normalWorld = TransformObjectToWorldNormal(IN.normal);
                
                // 正确获取阴影坐标
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                OUT.shadowCoords = GetShadowCoord(positionInputs);
                
                return OUT;
            }

            TEXTURE2D_ARRAY(_TerrainLayers);
            SAMPLER(sampler_TerrainLayers);
            float _UVScale;

            half4 frag (Varyings IN) : SV_Target
            {
                // 应用UV缩放
                float2 sampleUV = IN.positionWorld.xz * _UVScale;
                // 采样2D纹理数组
                half4 layerColor = SAMPLE_TEXTURE2D_ARRAY(_TerrainLayers, sampler_TerrainLayers, sampleUV, 2);
                
                // 获取主光照和阴影衰减,自动处理级联过渡
                Light mainLight = GetMainLight(IN.shadowCoords);
                half shadowAttenuation = mainLight.shadowAttenuation;
                
                // 简单漫反射计算(可选,增强光照效果)
                half3 normal = normalize(IN.normalWorld);
                half diffuse = saturate(dot(normal, mainLight.direction));
                half4 finalColor = layerColor * diffuse * mainLight.color * shadowAttenuation;
                
                return finalColor;
            }
            ENDHLSL
        }

        // URP阴影投射Pass
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normal : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
                float4 shadowCoord : TEXCOORD0;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                OUT.shadowCoord = GetShadowCoord(positionInputs);
                OUT.positionHCS = TransformWorldToHClip(ApplyShadowBias(positionInputs.positionWS, IN.normal));
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // 阴影投射输出
                return 0;
            }
            ENDHLSL
        }
    }
}

额外URP设置优化:

  • 在URP Asset的Quality面板中,调整Shadow Cascade的Split Distances,增加过渡区域长度
  • 开启Soft Shadows,并适当提高阴影分辨率

内容的提问来源于stack exchange,提问作者Aleksander Mendoza-Drosik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 13:22:36