421 lines
21 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 Context = GBuffer.MooaToonContext;
FMooaToonData MooaToonData = GBuffer.MooaToonContext.MooaToonData;
FDirectLighting Lighting = (FDirectLighting)0;
Context.LightColor *= PI_INV; // default(EV100=0) light intensity == PI
float3 LightColorAndAttenuation = AreaLight.FalloffColor * Context.LightColor * Falloff;
/* 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 SpecularIntensity = Pow2(GBuffer.Specular * 2);
const float3 H = normalize(V + L);
const float NoH = saturate(dot(N, H));
const float halfNoL = dot(N, L) * 0.5f + 0.5f;
const float2 BufferUV = SvPositionToBufferUV(float4(Context.PixelPos, 0, 0));
const float3 L_ClipSpace = mul(L, (float3x3)View.TranslatedWorldToClip).xyz;
const float2 L_ViewportSpace = normalize(L_ClipSpace.xy * float2(0.5, -0.5));
const float3 N_ClipSpace = mul(N, (float3x3)View.TranslatedWorldToClip).xyz;
const float2 N_ViewportSpace = normalize(N_ClipSpace.xy * float2(0.5, -0.5));
const float WorldUnitLengthInBufferSizePixels = ComputeWorldUnitLengthInBufferSizePixels(GBuffer.Depth);
// Diffuse
BRANCH if (Context.IsMainLight)
{ // Screen Space Depth Test Hair Shadow
float HairShadowValueOffset = 0;
BRANCH if(MooaToonData.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_FACE_SCREEN_SPACE_HAIR_SHADOW &&
MooaToonData.HairShadowWidth > 0 && MooaToonData.HairShadowIntensity > 0)
{ float2 UVOffset = L_ViewportSpace * WorldUnitLengthInBufferSizePixels * Pow2(MooaToonData.HairShadowWidth) * View.MooaHairShadowMaxScreenSpaceDistance;
FGBufferData HairGbuffer = GetGBufferData(BufferUV + UVOffset);
float DepthFade = 1 - saturate(max(0, HairGbuffer.Depth - GBuffer.Depth - View.MooaHairShadowDepthTestThreshold) / max(1e-5, View.MooaHairShadowDepthFadeDistance));
if (HairGbuffer.ShadingModelID == SHADINGMODELID_TOON &&
HairGbuffer.MooaToonContext.MooaToonData.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_HAIR)
{ HairShadowValueOffset = -1 * MooaToonData.HairShadowIntensity * DepthFade;
} }
Shadow.SurfaceShadow = saturate(Shadow.SurfaceShadow + HairShadowValueOffset + MooaToonData.MainLightShadowValueOffset * 2.0f - 1.0f);
LightColorAndAttenuation *= Shadow.SurfaceShadow;
Lighting.Diffuse = lerp(MooaToonData.MainLightShadowColor * lerp(1, Context.LightColor, MooaToonData.MainLightShadowApplyLightColor) * View.MooaIsGlobalIlluminationEnabled,
GBuffer.DiffuseColor * Context.LightColor, Shadow.SurfaceShadow);
} else
{
float OtherLightShadowValue = ToonStep(halfNoL, MooaToonData.OtherLightDiffuseFeather, MooaToonData.OtherLightDiffuseThreshold);
// Non-Directional light's SurfaceShadow contains distance attenuation, not just shadowmap
Shadow.SurfaceShadow *= OtherLightShadowValue;
LightColorAndAttenuation *= Shadow.SurfaceShadow;
Lighting.Diffuse = GBuffer.DiffuseColor * LightColorAndAttenuation;
}
// Anisotropy Context
#if SUPPORTS_ANISOTROPIC_MATERIALS
bool bHasAnisotropy = HasAnisotropy(GBuffer.SelectiveOutputMask);
#else
bool bHasAnisotropy = false;
#endif
BxDFContext AnisotropyContext = (BxDFContext)0;
{ half3 X = GBuffer.WorldTangent;
half3 Y = normalize(cross(N, X));
Init(AnisotropyContext, N, X, Y, V, L);
AnisotropyContext.NoV = saturate(abs( AnisotropyContext.NoV ) + 1e-5);
if (!bHasAnisotropy) GBuffer.Anisotropy = 0;
}
// Specular
BRANCH if (MooaToonData.IsPBRSpecular)
{ Lighting.Specular = LightColorAndAttenuation * NoL *
max(0, SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, AnisotropyContext, NoL, AreaLight));
} else
{
// Anisotropy
float2 XY = normalize(float2(1.0f + GBuffer.Anisotropy, 1.0f - GBuffer.Anisotropy));
float AnisotropyGradient = length(float2(XY.y * AnisotropyContext.XoH, XY.x * AnisotropyContext.YoH));
// Toon Specular: https://www.desmos.com/calculator/qecziyizl1
float SpecularGradient = 1.0f - AnisotropyGradient;
float SpecularThreshold = 1.0f - Pow2(MooaToonData.SpecularThreshold);
float SpecularFeather = Pow2(MooaToonData.SpecularFeather) * SpecularThreshold * 0.5f;
Lighting.Specular = saturate(ToonStep(SpecularGradient, SpecularFeather, SpecularThreshold + SpecularFeather, 1.0f, false, false))
* MooaToonData.SpecularColor * SpecularIntensity * LightColorAndAttenuation;
}
// Screen Space Depth Test Rim Light
// Recompute Normals to override DCC Normals on Face/Hair const float3 WorldNormalFromDepth = normalize(ReconstructNormalFromDepthBuffer_Copy(Context.PixelPos));
const float3 DepthToN_ClipSpace = mul(WorldNormalFromDepth, (float3x3)View.TranslatedWorldToClip).xyz;
const float2 DepthToN_ViewportSpace = DepthToN_ClipSpace.xy * float2(0.5, -0.5);
BRANCH if (MooaToonData.RimLightIntensity > 0 && MooaToonData.RimLightWidth > 0)
{ float NoL_ClipSpaceAngle01 = (dot(normalize(DepthToN_ClipSpace.xy), normalize(L_ClipSpace.xy)) * 0.5 + 0.5);
float WidthScaleByAngle = saturate(lerp(NoL_ClipSpaceAngle01, 1, MooaToonData.RimLightAngle));
const float MaxRimLightWidth = 0.0003 * View.MooaRimLightMaxWidth;
const float MaxDepthTestDistance = 100 * View.MooaRimLightMaxDepthTestDistance;
const float MaxDepthFadeDistance = 100 * View.MooaRimLightMaxDepthFadeDistance;
// TODO: AA
float SubPixelFade = 1;
float2 UVOffset = L_ViewportSpace * WorldUnitLengthInBufferSizePixels * MaxRimLightWidth * WidthScaleByAngle * Pow2(MooaToonData.RimLightWidth);
float2 TargetBufferUV = BufferUV + UVOffset;
#if MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE
// TranslucentBasePass.SceneDepth include Translucent First Layer Depth
// see UseFrontLayerReflection() float SingleLayerDeviceZ = Texture2DSampleLevel( TranslucentBasePass.SceneDepth, GlobalPointClampedSampler, TargetBufferUV, 0).r;
float NewDepth = ConvertFromDeviceZ(SingleLayerDeviceZ);
#else
float NewDepth = GetGBufferData(TargetBufferUV).Depth;
#endif
float DepthFade = saturate(max(0, NewDepth - GBuffer.Depth - MaxDepthTestDistance * Pow2(MooaToonData.RimLightDepthThreshold))
/ max(1e-5, MaxDepthFadeDistance * Pow2(MooaToonData.RimLightDepthThreshold)));
Lighting.Specular = max(Lighting.Specular, DepthFade * MooaToonData.RimLightIntensity * LightColorAndAttenuation);
}
return Lighting;
}
```