501 lines
22 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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.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。
## 数据结构体
```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);
}
//将合并后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
```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**)
- 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
```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);
}
}
```