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" } }
关键修改说明
- 新增Lightmap支持:添加了Lightmap相关属性、纹理采样器和宏定义,让Shader能读取烘焙后的光照贴图
- 分支处理光照逻辑:用
#if LIGHTMAP_ON区分实时和烘焙模式,烘焙时直接用Lightmap颜色替换原实时漫反射计算 - 优化阴影坐标计算:把阴影坐标计算从片元着色器移到顶点着色器,提升性能
- 适配URP管线:添加了URP烘焙所需的宏定义,确保Shader能识别烘焙数据
内容的提问来源于stack exchange,提问作者Rosta
相关产品推荐
相关产品推荐

