--- title: JasonMa渲染方案分析 date: 2024-01-25 16:02:26 excerpt: tags: rating: ⭐ --- # 前言 截止版本UE5.3.2。添加了一种新的GBuffer精度格式GBT_Uint_32_32_32_32,以及RT7。 修改内容: - GBufferPostDecode.ush:在GBufferPostDecode中给FGBufferData添加InitMooaToonContext()逻辑 - SceneRendering.cpp:Debug用的渲染变量调节,并且在FViewInfo::SetupUniformBufferParameters()添加设置相关变量的逻辑。 - 其他: - MaterialBaking:ExportMaterialProxy.h - MaterialEditor:MaterialEditor.cpp - PixelInspector:PixelInspectorResult.cpp、PixelInspectorResult.h # ToonShadingCommon.ush 除了一下工具函数,主要实现: 1. GBuffer的一些Encode / Decode Helper函数。 2. 用于存储数据的struct FMooaToonData、FMooaToonContext。 3. 针对上面2个结构体编码与解码的函数。FMooaCustomPayload用于光追Shader。 ## 数据结构体 ```c++ struct FMooaToonContext { FMooaToonData MooaToonData; bool IsMainLight; float3 LightColor; bool IsEditorPreviewWorldType; float Exposure; uint2 PixelPos; // SVPos.xy }; struct FMooaToonData { // Diffuse float3 MainLightShadowColor; float MainLightShadowValueOffset; float MainLightShadowApplyLightColor; float OtherLightDiffuseThreshold; float OtherLightDiffuseFeather; float GlobalIlluminationIntensity; float GlobalIlluminationDirectionality; // Specular float3 SpecularColor; float SpecularThreshold; float SpecularFeather; float ReflectionIntensity; bool IsPBRSpecular; float RimLightIntensity; float RimLightWidth; float RimLightAngle; float RimLightDepthThreshold; // Ray Tracing Shadow uint Stencil; uint RayTracingShadowFlag; float HairShadowWidth; float HairShadowIntensity; // Unused float CustomData3; }; ``` ## Encode/Decode 这里推荐使用计算器进行计算更加好理解。 ```c++ //取得指定Bits位数的最大数值,8位 => 255 uint GetBitsMaxValue(int Bits = 8) {return (1L << Bits) - 1;} //将2个uint合并1个uint,将Src1移动Scr2的位数后,将2者进行或运算合并到一起。 uint EncodeUint2ToUint(uint Src1, uint Src2, int BitsSrc1 = 8, int BitsSrc2 = 8) { return ((Src1 & GetBitsMaxValue(BitsSrc1)) << BitsSrc2) | (Src2 & GetBitsMaxValue(BitsSrc2)); } //将2个uint合并1个uint后转换成浮点数(0~1) float EncodeUint2ToFloat(uint Src1, uint Src2, int BitsSrc1 = 8, int BitsSrc2 = 8) { return float(EncodeUint2ToUint(Src1, Src2, BitsSrc1, BitsSrc2)) / GetBitsMaxValue(BitsSrc1 + BitsSrc2); } //将合并后Uint还原成2个uint void DecodeUint2FromUint(uint Src, out uint Dst1, out uint Dst2, int BitsDst1 = 8, int BitsDst2 = 8) { Dst1 = (Src >> BitsDst2) & GetBitsMaxValue(BitsDst1); Dst2 = Src & GetBitsMaxValue(BitsDst2); } //将合并后float(0~1)还原成2个uint void DecodeUint2FromFloat(float Src, out uint Dst1, out uint Dst2, int BitsDst1 = 8, int BitsDst2 = 8) { uint FloatToUint = uint(Src * GetBitsMaxValue(BitsDst1 + BitsDst2)); DecodeUint2FromUint(FloatToUint, Dst1, Dst2, BitsDst1, BitsDst2); } //将float(0~1)编码成uint uint EncodeFloatToUint(float Src1, int BitsSrc1 = 8) { return saturate(Src1) * GetBitsMaxValue(BitsSrc1); } uint EncodeFloat2ToUint(float Src1, float Src2, int BitsSrc1 = 8, int BitsSrc2 = 8) { return EncodeUint2ToUint(EncodeFloatToUint(Src1, BitsSrc1), EncodeFloatToUint(Src2, BitsSrc2), BitsSrc1, BitsSrc2); } float EncodeFloat2ToFloat(float Src1, float Src2, int BitsSrc1 = 8, int BitsSrc2 = 8) { return EncodeUint2ToFloat(EncodeFloatToUint(Src1, BitsSrc1), EncodeFloatToUint(Src2, BitsSrc2), BitsSrc1, BitsSrc2); } //将uint还原成float(0~1) float DecodeFloatFromUint(uint Src, int BitsDst1 = 8) { return float(Src & GetBitsMaxValue(BitsDst1)) / GetBitsMaxValue(BitsDst1); } void DecodeFloat2FromUint(uint Src, out float Dst1, out float Dst2, int BitsDst1 = 8, int BitsDst2 = 8) { uint OutUint1, OutUint2; DecodeUint2FromUint(Src, OutUint1, OutUint2, BitsDst1, BitsDst2); Dst1 = DecodeFloatFromUint(OutUint1, BitsDst1); Dst2 = DecodeFloatFromUint(OutUint2, BitsDst2); } void DecodeFloat2FromFloat(float Src, out float Dst1, out float Dst2, int BitsDst1 = 8, int BitsDst2 = 8) { uint OutUint1, OutUint2; DecodeUint2FromFloat(Src, OutUint1, OutUint2, BitsDst1, BitsDst2); Dst1 = DecodeFloatFromUint(OutUint1, BitsDst1); Dst2 = DecodeFloatFromUint(OutUint2, BitsDst2); ``` ## 结构体与GBuffer Encode/Decode ```c++ void EncodeMooaToonData(FMooaToonData MooaToonData, out float4 MooaToonDataA, out float4 MooaToonDataB, out float4 MooaToonDataC) { MooaToonDataA = (float4)0; MooaToonDataB = (float4)0; MooaToonDataC = (float4)0; MooaToonDataA.x = EncodeFloat2ToUint(MooaToonData.OtherLightDiffuseThreshold, MooaToonData.OtherLightDiffuseFeather); MooaToonDataA.y = EncodeFloat2ToUint(MooaToonData.GlobalIlluminationIntensity, MooaToonData.GlobalIlluminationDirectionality, 6, 6); MooaToonDataA.z = EncodeFloat2ToUint(MooaToonData.MainLightShadowApplyLightColor, MooaToonData.CustomData3, 6, 4); MooaToonDataA.w = EncodeUint2ToUint( EncodeFloatToUint(MooaToonData.HairShadowWidth, 6), EncodeUint2ToUint( MooaToonData.Stencil, EncodeUint2ToUint(MooaToonData.RayTracingShadowFlag, MooaToonData.IsPBRSpecular ? 1 : 0, 2, 1), 5, 3), 6, 8); MooaToonDataB.x = EncodeFloat2ToUint(MooaToonData.SpecularColor.r, MooaToonData.SpecularColor.g); MooaToonDataB.y = EncodeUint2ToUint( EncodeFloatToUint(MooaToonData.SpecularColor.b), EncodeFloat2ToUint(MooaToonData.ReflectionIntensity, MooaToonData.HairShadowIntensity, 6, 6), 8, 12); MooaToonDataB.z = EncodeUint2ToUint( EncodeFloat2ToUint(MooaToonData.SpecularThreshold, MooaToonData.SpecularFeather), EncodeFloatToUint(MooaToonData.RimLightIntensity, 6), 16, 6); MooaToonDataB.w = EncodeUint2ToUint( EncodeFloat2ToUint(MooaToonData.RimLightWidth, MooaToonData.RimLightAngle, 6, 6), EncodeFloatToUint(MooaToonData.RimLightDepthThreshold, 6), 12, 6); MooaToonDataC.rgb = saturate(MooaToonData.MainLightShadowColor); MooaToonDataC.a = saturate(MooaToonData.MainLightShadowValueOffset); } FMooaToonData DecodeMooaToonData(float4 MooaToonDataA, float4 MooaToonDataB, float4 MooaToonDataC) { FMooaToonData MooaToonData = (FMooaToonData)0; uint Out0, Out1, Out2, Out3; DecodeFloat2FromUint(MooaToonDataA.x, MooaToonData.OtherLightDiffuseThreshold, MooaToonData.OtherLightDiffuseFeather); DecodeFloat2FromUint(MooaToonDataA.y, MooaToonData.GlobalIlluminationIntensity, MooaToonData.GlobalIlluminationDirectionality, 6, 6); DecodeFloat2FromUint(MooaToonDataA.z, MooaToonData.MainLightShadowApplyLightColor, MooaToonData.CustomData3, 6, 4); DecodeUint2FromUint(MooaToonDataA.w, Out0, Out1, 6, 8); MooaToonData.HairShadowWidth = DecodeFloatFromUint(Out0, 6); DecodeUint2FromUint(Out1, MooaToonData.Stencil, Out2, 5, 3); DecodeUint2FromUint(Out2, MooaToonData.RayTracingShadowFlag, MooaToonData.IsPBRSpecular, 2, 1); DecodeFloat2FromUint(MooaToonDataB.x, MooaToonData.SpecularColor.r, MooaToonData.SpecularColor.g); DecodeUint2FromUint(MooaToonDataB.y, Out0, Out1, 8, 12); MooaToonData.SpecularColor.b = DecodeFloatFromUint(Out0); DecodeFloat2FromUint(Out1, MooaToonData.ReflectionIntensity, MooaToonData.HairShadowIntensity, 6, 6); DecodeUint2FromUint(MooaToonDataB.z, Out0, Out1, 16, 6); DecodeFloat2FromUint(Out0, MooaToonData.SpecularThreshold, MooaToonData.SpecularFeather); MooaToonData.RimLightIntensity = DecodeFloatFromUint(Out1, 6); DecodeUint2FromUint(MooaToonDataB.w, Out0, Out1, 12, 6); DecodeFloat2FromUint(Out0, MooaToonData.RimLightWidth, MooaToonData.RimLightAngle, 6, 6); MooaToonData.RimLightDepthThreshold = DecodeFloatFromUint(Out1, 6); MooaToonData.MainLightShadowColor = MooaToonDataC.rgb; MooaToonData.MainLightShadowValueOffset = MooaToonDataC.a; return MooaToonData; } uint4 EncodeMooaToonDataToBuffer(FMooaToonData MooaToonData) { MooaToonData.SpecularColor = LinearToSrgb(saturate(MooaToonData.SpecularColor)); float4 MooaToonDataA, MooaToonDataB, MooaToonDataC; uint4 Out; EncodeMooaToonData(MooaToonData, MooaToonDataA, MooaToonDataB, MooaToonDataC); Out.x = EncodeUint2ToUint(MooaToonDataA.x, MooaToonDataB.x, 16, 16); Out.y = EncodeUint2ToUint(MooaToonDataA.y, MooaToonDataB.y, 12, 20); Out.z = EncodeUint2ToUint(MooaToonDataA.z, MooaToonDataB.z, 10, 22); Out.w = EncodeUint2ToUint(MooaToonDataA.w, MooaToonDataB.w, 14, 18); return Out; } FMooaToonData DecodeMooaToonDataFromBuffer(uint4 ToonBufferA, float4 CustomData) { uint4 Out0, Out1; DecodeUint2FromUint(ToonBufferA.x, Out0.x, Out1.x, 16, 16); DecodeUint2FromUint(ToonBufferA.y, Out0.y, Out1.y, 12, 20); DecodeUint2FromUint(ToonBufferA.z, Out0.z, Out1.z, 10, 22); DecodeUint2FromUint(ToonBufferA.w, Out0.w, Out1.w, 14, 18); float4 MooaToonDataA = Out0; float4 MooaToonDataB = Out1; FMooaToonData MooaToonData = DecodeMooaToonData(MooaToonDataA, MooaToonDataB, CustomData); MooaToonData.MainLightShadowColor = sRGBToLinear(MooaToonData.MainLightShadowColor); MooaToonData.SpecularColor = sRGBToLinear(MooaToonData.SpecularColor); return MooaToonData; } ``` # BasePassPixelShader.usf ## GBuffer ```c++ /* * CustomData (GBufferD) (MP_MooaToonDataC) (0~1) = MainLightShadowColor(8 8 8) MainLightShadowValueOffset(8) * * ToonBufferA (RGBA Uint 32) * xyzw: MooaToonDataA.xyzw | MooaToonDataB.xyzw (32 bits total) * * MP_MooaToonDataA (float4, max storage 23 bits uint) * x: OtherLightDiffuseThreshold(8) OtherLightDiffuseFeather(8) * y: GlobalIlluminationIntensity(6) GlobalIlluminationDirectionality(6) * z: MainLightShadowApplyLightColor(6) CustomData3(4) * w: HairShadowWidth(6) Stencil(5) RayTracingShadowFlag(2) IsPBRSpecular(1) * * MP_MooaToonDataB (float4, max storage 23 bits uint) * x: SpecularColor.x(8) SpecularColor.y(8) * y: SpecularColor.z(8) ReflectionIntensity(6) HairShadowIntensity(6) * z: SpecularThreshold(8) SpecularFeather(8) RimLightIntensity(6) * w: RimLightWidth(6) RimLightAngle(6) RimLightDepthThreshold(6) * * * Tips: store sRGB Color to maximize accuracy */ ``` >PS.float4, max storage 23 bits uint >Jason自己添加了GBT_Uint_32_32_32_32的GBuffer格式。 - MooaToonDataA (RGBA Uint 32 **ToonBufferA**) - R(4+4):OtherLightDiffuseThreshold + OtherLightDiffuseFeather - G(4+4):GlobalIlluminationIntensity + GlobalIlluminationDirectionality - B(4+4):MainLightShadowApplyLightColor + CustomData3 - A (6+5+2+1):HairShadowWidth + Stencil + RayTracingShadowFlag + IsPBRSpecular - MooaToonDataB (RGBA Uint 32,从材质直接传递到FGBuffer结构体中,位置位于BasePassPixelShader.usf的InitMooaToonData与InitMooaToonContext,其他可能位置位于GBufferHelpers.ush的GBufferPostDecode()、DeferredShadingCommon.ush的DecodeGBufferData()) - R(8+8):SpecularColor.x + SpecularColor.y - G(8+6+6):SpecularColor.z + ReflectionIntensity + HairShadowIntensity - B(8+8+6):SpecularThreshold + SpecularFeather + RimLightIntensity - A(6+6+6):RimLightWidth + RimLightAngle + RimLightDepthThreshold(6) - MooaToonDataC(**GBufferD CustomData**) = MainLightShadowColor(8 8 8) + MainLightShadowValueOffset(8) # ShaderMoodel ```c++ /* ================================== Mooa Toon Deferred Lighting Model ====================================================================================================== * IndirectColor                (DiffuseIndirectComposite.usf)    = IndirectDiffuse + IndirectSpecular * DirectionalLightShadingColor (DeferredLightPixelShaders.usf)    = (lerp(ShadowColor, Diffuse * LightColor, min(ShadowValue, LightAttenuation)) + Specular * LightColor) * MooaExposure * OtherLightShadingColor        (DeferredLightPixelShaders.usf)    = (Diffuse + Specular) * LightColor * min(ShadowValue, LightAttenuation) * OtherLightCount * FinalColor                                                    = IndirectColor + DirectionalLightShadingColor + OtherLightShadingColor * ================================================================================================================================================================== */ // Mooa Indirect Lighting if (Material.GBufferData.ShadingModelID == SHADINGMODELID_TOON) { float3 SHDiffuseAverage = GetSkySHDiffuse(0.0f) * View.SkyLightColor.rgb * View.PreExposure; float3 LumenGiDiffuse = IndirectLighting.Diffuse; FMooaToonData MooaToonData = Material.GBufferData.MooaToonContext.MooaToonData; OutAddColor.rgb = //MooaToonData.MainLightShadowColor + lerp(SHDiffuseAverage * Material.GBufferData.BaseColor, LumenGiDiffuse, MooaToonData.GlobalIlluminationDirectionality) * MooaToonData.GlobalIlluminationIntensity; OutAddColor.rgb += IndirectLighting.Specular * Pow2(MooaToonData.ReflectionIntensity); OutAddColor.rgb *= View.MooaExposureScale; OutAddColor.a = Luminance(OutAddColor.rgb); } else { IndirectLighting.Specular *= GetSSSCheckerboadSpecularScale(PixelPos, Material.bNeedsSeparateLightAccumulation); FLightAccumulator LightAccumulator = (FLightAccumulator)0; LightAccumulator_Add( LightAccumulator, IndirectLighting.Diffuse + IndirectLighting.Specular, IndirectLighting.Diffuse, 1.0f, Material.bNeedsSeparateLightAccumulation); OutAddColor = LightAccumulator_GetResult(LightAccumulator); } ``` ```c++ FDirectLighting ToonBxDF(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow) { FMooaToonContext MooaToonContext = GBuffer.MooaToonContext; FToonGBufferData ToonGBuffer = GBuffer.MooaToonContext.ToonGBuffer; FDirectLighting Lighting = (FDirectLighting)0; float3 LightColorAndAttenuation = AreaLight.FalloffColor * MooaToonContext.LightColor * Falloff; ColorSaturationPowerAndScale(LightColorAndAttenuation, View.MooaLightSaturationScale); /* Tips: * SvPosition == PixelPos (0 ~ View.BufferSizeAndInvSize.xy) * SceneTextureUV == BufferUV == ScreenUV (0 ~ 1), used to GetScreenSpaceData() / GetGBufferData() * * ScreenPosition = ClipPosition.xy / ClipPosition.w * ViewportUV = ScreenPosition * float2(0.5, -0.5) + 0.5 * ViewportUV is the visible part of Buffer's UV (0 ~ 1) */ const float NoL_Full = dot(N, L); const float NoL_Half = NoL_Full * 0.5f + 0.5f; const float3 H = normalize(V + L); const float2 BufferUV = SvPositionToBufferUV(float4(MooaToonContext.PixelPos, 0, 0)); const float2 ViewportUV = BufferUVToViewportUV(BufferUV); const float3 L_ClipSpace = mul(L, (float3x3)View.TranslatedWorldToClip).xyz; const float2 L_ViewportSpace = (L_ClipSpace.xy * float2(0.5, -0.5)); const float3 N_ClipSpace = mul(N, (float3x3)View.TranslatedWorldToClip).xyz; const float2 N_ViewportSpace = (N_ClipSpace.xy * float2(0.5, -0.5)); const float ViewportSpaceToWorldSpaceDir = rcp(GBuffer.Depth); // Diffuse { float HairShadowOffset = 0; float DiffuseColorRampUVOffset = (ToonGBuffer.DiffuseColorRampUVOffset * 2.0f - 1.0f) * View.MooaDiffuseColorRampUVOffsetMaxRange; float ShadowGradient = saturate(NoL_Half + DiffuseColorRampUVOffset); #if SHADING_PATH_DEFERRED && defined(MOOA_TOON_DEFERRED_LIGHTING) && !SUBSTRATE_ENABLED { // Screen Space Depth Test Hair Shadow const float HairShadowWidth = 2.0f * View.MooaHairShadowWidth; const float HairShadowIntensity = View.MooaHairShadowIntensity; BRANCH if(ToonGBuffer.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_FACE_SCREEN_SPACE_HAIR_SHADOW && HairShadowWidth > 0 && HairShadowIntensity > 0) { float2 ViewportUVOffset = L_ViewportSpace * ViewportSpaceToWorldSpaceDir * HairShadowWidth; float2 TargetBufferUV = ViewportUVToBufferUV(saturate(ViewportUV + ViewportUVOffset)); FGBufferData HairGbuffer = GetGBufferData(TargetBufferUV); float DepthFade = saturate(max(0, GBuffer.Depth - HairGbuffer.Depth - View.MooaHairShadowDepthTestThreshold) / max(1e-5, View.MooaHairShadowDepthTestFadeDistance)); if (HairGbuffer.ShadingModelID == SHADINGMODELID_TOON && HairGbuffer.MooaToonContext.ToonGBuffer.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_HAIR) { HairShadowOffset = -1 * HairShadowIntensity * DepthFade; } } } #endif GetDistanceFieldFacialShadow(GBuffer, ToonGBuffer, L, DiffuseColorRampUVOffset, ShadowGradient); float DiffuseColorRampU = min3(saturate(Shadow.SurfaceShadow + HairShadowOffset), GBuffer.GBufferAO, ShadowGradient); half4 DiffuseColorRamp = SampleGlobalRamp(View.MooaGlobalDiffuseColorRampAtlas, DiffuseColorRampU, ToonGBuffer.DiffuseColorRampIndex, View.MooaGlobalDiffuseColorRampAtlasHeight); Shadow.SurfaceShadow = DiffuseColorRampU; half3 ToonDiffuseColor = GBuffer.DiffuseColor; half3 ToonShadowColor = ToonGBuffer.MainLightShadowColor * (1 - GBuffer.Metallic) * GetShadowColorIntensity(MooaToonContext); #if USE_DEVELOPMENT_SHADERS ToonShadowColor *= View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz; #endif ToonDiffuseColor = lerp(ToonShadowColor, ToonDiffuseColor, DiffuseColorRamp.a); Lighting.Diffuse = Diffuse_Lambert(ToonDiffuseColor) * DiffuseColorRamp.rgb * LightColorAndAttenuation; } // Anisotropy Specular BxDF Context bool bHasAnisotropy; BxDFContext Context = (BxDFContext)0; { #if SUPPORTS_ANISOTROPIC_MATERIALS bHasAnisotropy = true;// HasAnisotropy(GBuffer.SelectiveOutputMask); #else bHasAnisotropy = false; #endif if (ToonGBuffer.ShadingFeatureID == MOOA_SHADING_FEATURE_ID_DISTANCE_FIELD_FACIAL_SHADOW) bHasAnisotropy = false; BRANCH if (bHasAnisotropy) { half3 X = GBuffer.WorldTangent; half3 Y = normalize(cross(N, X)); Init(Context, N, X, Y, V, L); } else { Init(Context, N, V, L); GBuffer.Anisotropy = 0; } Context.NoV = saturate(abs( Context.NoV ) + 1e-5); } // Specular BRANCH if (ToonGBuffer.ShadingFeatureID == MOOA_SHADING_FEATURE_ID_PBR_SPECULAR) { #if SUPPORTS_ANISOTROPIC_MATERIALS BRANCH if (bHasAnisotropy) { Lighting.Specular = LightColorAndAttenuation * Shadow.SurfaceShadow * NoL * SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, Context, NoL, AreaLight); } else #endif { BRANCH if( IsRectLight(AreaLight) ) { Lighting.Specular = MooaToonContext.LightColor * Shadow.SurfaceShadow * RectGGXApproxLTC(GBuffer.Roughness, GBuffer.SpecularColor, N, V, AreaLight.Rect, AreaLight.Texture); } else { Lighting.Specular = LightColorAndAttenuation * Shadow.SurfaceShadow * NoL * SpecularGGX(GBuffer.Roughness, GBuffer.SpecularColor, Context, NoL, AreaLight); } } } else { float MaxSpecularValue; float SpecularColorRampU = GetSpecularColorRampUAndMaxSpecularValue(GBuffer, Context, N, H, MaxSpecularValue); float SpecularColorRampUVOffset = (ToonGBuffer.SpecularColorRampUVOffset * 2.0f - 1.0f) * View.MooaSpecularColorRampUVOffsetMaxRange; half3 SpecularColor = SampleGlobalRamp(View.MooaGlobalSpecularColorRampAtlas, SpecularColorRampU + SpecularColorRampUVOffset, ToonGBuffer.SpecularColorRampIndex, View.MooaGlobalSpecularColorRampAtlasHeight).rgb; Lighting.Specular = GBuffer.SpecularColor * ToonGBuffer.SpecularColor * MaxSpecularValue * SpecularColor * LightColorAndAttenuation * Shadow.SurfaceShadow; } // Rimlight float3 RimLight = GetScreenSpaceDepthTestRimlightColor(GBuffer, ToonGBuffer, ViewportUV, L_ViewportSpace, ViewportSpaceToWorldSpaceDir) * LightColorAndAttenuation * Shadow.SurfaceShadow; ColorSaturationPowerAndScale(RimLight, View.MooaRimLightSaturationScale, View.MooaRimLightIntensity); Lighting.Specular += RimLight; return Lighting; } ``` ## Diffuse ```c++ ...    float3 LightColorAndAttenuation = AreaLight.FalloffColor * MooaToonContext.LightColor * Falloff; ... ... // Diffuse BRANCH if (Context.IsMainLight) { Shadow.SurfaceShadow = saturate(Shadow.SurfaceShadow + HairShadowValueOffset + MooaToonData.MainLightShadowValueOffset * 2.0f - 1.0f); LightColorAndAttenuation *= Shadow.SurfaceShadow; float3 MainLightShadowColor = MooaToonData.MainLightShadowColor * lerp(1, Context.LightColor, MooaToonData.MainLightShadowApplyLightColor) * View.MooaIsGlobalIlluminationEnabled; Lighting.Diffuse = lerp(MainLightShadowColor, GBuffer.BaseColor * Context.LightColor, Shadow.SurfaceShadow) * PI_INV; } else { float OtherLightShadowValue = ToonStep(halfNoL, MooaToonData.OtherLightDiffuseFeather, MooaToonData.OtherLightDiffuseThreshold); // Non-Directional light's SurfaceShadow contains distance attenuation, not just shadowmap Shadow.SurfaceShadow *= saturate(OtherLightShadowValue + HairShadowValueOffset); LightColorAndAttenuation *= Shadow.SurfaceShadow; Lighting.Diffuse = GBuffer.BaseColor * LightColorAndAttenuation * PI_INV; } ``` # 实用函数 如果需要脸部SDF阴影,就覆盖ShadowGradient。 ```c++ void GetDistanceFieldFacialShadow(FGBufferData GBuffer, FToonGBufferData ToonGBuffer, float3 L, float DiffuseColorRampUVOffset, inout float ShadowGradient) { BRANCH if (ToonGBuffer.ShadingFeatureID == MOOA_SHADING_FEATURE_ID_DISTANCE_FIELD_FACIAL_SHADOW) { float3 FaceForwardDir = GBuffer.WorldTangent; float LightAngle = RadianToDegree(FastACos(dot(FaceForwardDir, L))); float RampUVOffsetByLightAngle = (1.0 - clamp(LightAngle / 180.0f, 1e-4, 1 - 1e-4)) - 0.5f; bool bLightAtRight = cross(FaceForwardDir, L).z >= 0; // TODO: Fix the numerical accuracy issue of extreme angles float shadowSdf = bLightAtRight ? ToonGBuffer.FacialShadowSdfRight : ToonGBuffer.FacialShadowSdfLeft; ShadowGradient = saturate(RampUVOffsetByLightAngle + shadowSdf + DiffuseColorRampUVOffset); } } ```