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

Unity URP自定义卡通Shader烘焙光照后物体变黑问题求助

URP卡通Shader烘焙后物体变黑问题解决

问题描述

我有一个实现卡通效果的Unity(URP)Shader,场景光照烘焙前显示正常且效果良好,但烘焙完成后,使用该Shader的物体直接变为黑色,我没有编写Shader的经验,无法自行解决。

问题根源

这个Shader只适配了实时主光源,完全没处理烘焙后的光照数据:

  • 烘焙后静态主光源会被禁用,_MainLightColor、_MainLightPosition这类实时光源变量会返回0,导致漫反射计算完全失效
  • 环境光仅依赖球谐光照(SampleSH),没有读取烘焙的Lightmap/光照探针数据,静态物体丢失所有环境光照输入
  • 缺少URP烘焙光照所需的宏定义和采样逻辑,无法识别烘焙后的光照信息

修复方案(修改后的完整Shader)

下面是修复后的Shader代码,已适配实时/烘焙两种光照模式:

Shader "Lpk/LightModel/ToonLightBase"
{
    Properties
    {
        _BaseMap            ("Texture", 2D)                       = "white" {}
        _BaseColor          ("Color", Color)                      = (0.5,0.5,0.5,1)
        
        [Space]
        _ShadowStep         ("ShadowStep", Range(0, 1))           = 0.5
        _ShadowStepSmooth   ("ShadowStepSmooth", Range(0, 1))     = 0.04
        
        [Space] 
        _SpecularStep       ("SpecularStep", Range(0, 1))         = 0.6
        _SpecularStepSmooth ("SpecularStepSmooth", Range(0, 1))   = 0.05
        [HDR]_SpecularColor ("SpecularColor", Color)              = (1,1,1,1)
        
        [Space]
        _RimStep            ("RimStep", Range(0, 1))              = 0.65
        _RimStepSmooth      ("RimStepSmooth",Range(0,1))          = 0.4
        _RimColor           ("RimColor", Color)                   = (1,1,1,1)
        
        [Space]   
        _OutlineWidth      ("OutlineWidth", Range(0.0, 1.0))      = 0.15
        _OutlineColor      ("OutlineColor", Color)                = (0.0, 0.0, 0.0, 1)

        // 新增:Lightmap相关属性(URP会自动填充)
        [HideInInspector] _LightmapColor ("Lightmap Color", Color) = (1,1,1,1)
        [HideInInspector] _LightmapST ("Lightmap ST", Vector) = (1,1,0,0)
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "LightMode" = "UniversalForward" }
        
        Pass
        {
            Name "UniversalForward"
            Tags
            {
                "LightMode" = "UniversalForward"
            }
            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x

            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            // 新增:适配烘焙光照的宏定义
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile _ DIRLIGHTMAP_COMBINED
            #pragma multi_compile _ DYNAMICLIGHTMAP_ON
            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
             
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
            // 新增:包含Lightmap库
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lightmap.hlsl"

            TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
            // 新增:Lightmap纹理采样器
            TEXTURE2D(_Lightmap); SAMPLER(sampler_Lightmap);
            TEXTURE2D(_DynamicLightmap); SAMPLER(sampler_DynamicLightmap);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseColor;
                float _ShadowStep;
                float _ShadowStepSmooth;
                float _SpecularStep;
                float _SpecularStepSmooth;
                float4 _SpecularColor;
                float _RimStepSmooth;
                float _RimStep;
                float4 _RimColor;
                // 新增:Lightmap参数
                float4 _LightmapColor;
                float4 _LightmapST;
            CBUFFER_END

            struct Attributes
            {     
                float4 positionOS   : POSITION;
                float3 normalOS     : NORMAL;
                float4 tangentOS    : TANGENT;
                float2 uv           : TEXCOORD0;
                // 新增:Lightmap UV
                float2 uvLM         : TEXCOORD1;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            }; 

            struct Varyings
            {
                float2 uv            : TEXCOORD0;
                float4 normalWS      : TEXCOORD1;    // xyz: normal, w: viewDir.x
                float4 tangentWS     : TEXCOORD2;    // xyz: tangent, w: viewDir.y
                float4 bitangentWS   : TEXCOORD3;    // xyz: bitangent, w: viewDir.z
                float3 viewDirWS     : TEXCOORD4;
                float4 shadowCoord   : TEXCOORD5;   // shadow receive 
                float4 fogCoord      : TEXCOORD6;   
                float3 positionWS    : TEXCOORD7;
                // 新增:传递Lightmap UV
                float2 uvLM          : TEXCOORD8;
                float4 positionCS    : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;
                    
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_TRANSFER_INSTANCE_ID(input, output);

                VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
                VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
                float3 viewDirWS = GetCameraPositionWS() - vertexInput.positionWS;

                output.positionCS = vertexInput.positionCS;
                output.positionWS = vertexInput.positionWS;
                output.uv = input.uv;
                // 新增:处理Lightmap UV
                output.uvLM = input.uvLM.xy * _LightmapST.xy + _LightmapST.zw;
                output.normalWS = float4(normalInput.normalWS, viewDirWS.x);
                output.tangentWS = float4(normalInput.tangentWS, viewDirWS.y);
                output.bitangentWS = float4(normalInput.bitangentWS, viewDirWS.z);
                output.viewDirWS = viewDirWS;
                output.fogCoord = ComputeFogFactor(output.positionCS.z);
                // 移动阴影坐标计算到顶点着色器(原片元着色器计算会导致性能问题)
                output.shadowCoord = TransformWorldToShadowCoord(output.positionWS);
                return output;
            }
            
            half remap(half x, half t1, half t2, half s1, half s2)
            {
                return (x - t1) / (t2 - t1) * (s2 - s1) + s1;
            }
            
            float4 frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(input);

                float2 uv = input.uv;
                float3 N = normalize(input.normalWS.xyz);
                float3 V = normalize(input.viewDirWS.xyz);
                float3 L = normalize(_MainLightPosition.xyz);
                float3 H = normalize(V+L);
                
                float NV = dot(N,V);
                float NH = dot(N,H);
                float NL = dot(N,L);
                
                NL = NL * 0.5 + 0.5;

                float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);

                float specularNH = smoothstep((1-_SpecularStep * 0.05)  - _SpecularStepSmooth * 0.05, (1-_SpecularStep* 0.05)  + _SpecularStepSmooth * 0.05, NH) ;
                float shadowNL = smoothstep(_ShadowStep - _ShadowStepSmooth, _ShadowStep + _ShadowStepSmooth, NL);

                //shadow
                float shadow = MainLightRealtimeShadow(input.shadowCoord);
                
                //rim
                float rim = smoothstep((1-_RimStep) - _RimStepSmooth * 0.5, (1-_RimStep) + _RimStepSmooth * 0.5, 0.5 - NV);
                
                // --- 修改:适配实时/烘焙光照 ---
                float3 diffuse = 0;
                #if LIGHTMAP_ON
                    // 采样烘焙的Lightmap
                    float3 lightmapColor = SampleLightmap(input.uvLM);
                    diffuse = lightmapColor * baseMap.rgb * _BaseColor.rgb;
                #else
                    // 原实时光照逻辑
                    diffuse = _MainLightColor.rgb * baseMap.rgb * _BaseColor.rgb * shadowNL * shadow;
                #endif
                
                //specular 保持原逻辑(仅实时光源生效,如需烘焙高光需额外处理)
                float3 specular = _SpecularColor.rgb * shadow * shadowNL * specularNH;
                
                //ambient 补充Lightmap环境光
                float3 ambient = rim * _RimColor.rgb;
                #if !LIGHTMAP_ON
                    ambient += SampleSH(N) * _BaseColor.rgb * baseMap.rgb;
                #endif
            
                float3 finalColor = diffuse + ambient + specular;
                finalColor = MixFog(finalColor, input.fogCoord);
                return float4(finalColor , 1.0);
            }
            ENDHLSL
        }
        
        //Outline
        Pass
        {
            Name "Outline"
            Cull Front
            Tags
            {
                "LightMode" = "SRPDefaultUnlit"
            }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f
            {
                float4 pos      : SV_POSITION;
                float4 fogCoord : TEXCOORD0;    
            };
            
            float _OutlineWidth;
            float4 _OutlineColor;
            
            v2f vert(appdata v)
            {
                 v2f o;

                // object -> world
                float3 worldPos = TransformObjectToWorld(v.vertex.xyz);
                float3 worldNormal = TransformObjectToWorldNormal(v.normal);

                // extrusion in world space (static-safe)
                worldPos += normalize(worldNormal) * _OutlineWidth * 0.1;

                // world -> clip
                o.pos = TransformWorldToHClip(worldPos);

                // fog
                o.fogCoord = ComputeFogFactor(o.pos.z);

                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                float3 finalColor = MixFog(_OutlineColor, i.fogCoord);
                return float4(finalColor,1.0);
            }
            
            ENDHLSL
        }
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}

关键修改说明

  1. 新增Lightmap支持:添加了Lightmap相关属性、纹理采样器和宏定义,让Shader能读取烘焙后的光照贴图
  2. 分支处理光照逻辑:用#if LIGHTMAP_ON区分实时和烘焙模式,烘焙时直接用Lightmap颜色替换原实时漫反射计算
  3. 优化阴影坐标计算:把阴影坐标计算从片元着色器移到顶点着色器,提升性能
  4. 适配URP管线:添加了URP烘焙所需的宏定义,确保Shader能识别烘焙数据

内容的提问来源于stack exchange,提问作者Rosta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 14:05:55