2024-01-25 19:31:07 +08:00
|
|
|
|
---
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2024-01-25 20:10:54 +08:00
|
|
|
|
# ShaderMoodel
|
2024-01-25 19:31:07 +08:00
|
|
|
|
```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);
|
|
|
|
|
}
|
2024-01-25 20:10:54 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```c++
|
2025-02-06 21:31:49 +08:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2025-02-06 22:31:20 +08:00
|
|
|
|
## Diffuse
|
|
|
|
|
```c++
|
|
|
|
|
...
|
|
|
|
|
float3 LightColorAndAttenuation = AreaLight.FalloffColor * MooaToonContext.LightColor * Falloff;
|
|
|
|
|
...
|
|
|
|
|
...
|
2025-02-08 13:12:05 +08:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2025-02-06 22:31:20 +08:00
|
|
|
|
```
|
|
|
|
|
|
2025-02-06 21:31:49 +08:00
|
|
|
|
# 实用函数
|
|
|
|
|
如果需要脸部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);
|
|
|
|
|
}
|
2024-01-25 20:10:54 +08:00
|
|
|
|
}
|
2025-02-06 21:31:49 +08:00
|
|
|
|
```
|
|
|
|
|
|