22 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
JasonMa渲染方案分析 2024-01-25 16:02:26

前言

截止版本UE5.3.2。添加了一种新的GBuffer精度格式GBT_Uint_32_32_32_32以及RT7。

修改内容:

  • GBufferPostDecode.ush在GBufferPostDecode中给FGBufferData添加InitMooaToonContext()逻辑
  • SceneRendering.cppDebug用的渲染变量调节并且在FViewInfo::SetupUniformBufferParameters()添加设置相关变量的逻辑。
  • 其他:
    • MaterialBakingExportMaterialProxy.h
    • MaterialEditorMaterialEditor.cpp
    • PixelInspectorPixelInspectorResult.cpp、PixelInspectorResult.h

ToonShadingCommon.ush

除了一下工具函数,主要实现:

  1. GBuffer的一些Encode / Decode Helper函数。
  2. 用于存储数据的struct FMooaToonData、FMooaToonContext。
  3. 针对上面2个结构体编码与解码的函数。FMooaCustomPayload用于光追Shader。

数据结构体

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

这里推荐使用计算器进行计算更加好理解。

//取得指定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);  
}  

//将合并后float0~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);  
}  
  
//将float0~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还原成float0~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

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

/*
 * 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)
    • R4+4OtherLightDiffuseThreshold + OtherLightDiffuseFeather
    • G4+4GlobalIlluminationIntensity + GlobalIlluminationDirectionality
    • B4+4MainLightShadowApplyLightColor + CustomData3
    • A 6+5+2+1HairShadowWidth + Stencil + RayTracingShadowFlag + IsPBRSpecular
  • MooaToonDataB (RGBA Uint 32从材质直接传递到FGBuffer结构体中位置位于BasePassPixelShader.usf的InitMooaToonData与InitMooaToonContext其他可能位置位于GBufferHelpers.ush的GBufferPostDecode()、DeferredShadingCommon.ush的DecodeGBufferData())
    • R8+8SpecularColor.x + SpecularColor.y
    • G8+6+6SpecularColor.z + ReflectionIntensity + HairShadowIntensity
    • B8+8+6SpecularThreshold + SpecularFeather + RimLightIntensity
    • A6+6+6RimLightWidth + RimLightAngle + RimLightDepthThreshold(6)
  • MooaToonDataC(GBufferD CustomData) = MainLightShadowColor(8 8 8) + MainLightShadowValueOffset(8)

ShaderMoodel

/* ================================== 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);  
}

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

...   
	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。

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);
	}
}