vault backup: 2025-02-06 21:31:49

This commit is contained in:
BlueRose 2025-02-06 21:31:49 +08:00
parent c21c070784
commit 8e9daf7717

View File

@ -311,111 +311,167 @@ else
``` ```
```c++ ```c++
FDirectLighting ToonBxDF(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow) FDirectLighting ToonBxDF(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow)
{ {
FMooaToonContext Context = GBuffer.MooaToonContext; FMooaToonContext MooaToonContext = GBuffer.MooaToonContext;
FMooaToonData MooaToonData = GBuffer.MooaToonContext.MooaToonData; FToonGBufferData ToonGBuffer = GBuffer.MooaToonContext.ToonGBuffer;
FDirectLighting Lighting = (FDirectLighting)0; FDirectLighting Lighting = (FDirectLighting)0;
Context.LightColor *= PI_INV; // default(EV100=0) light intensity == PI float3 LightColorAndAttenuation = AreaLight.FalloffColor * MooaToonContext.LightColor * Falloff;
float3 LightColorAndAttenuation = AreaLight.FalloffColor * Context.LightColor * Falloff; ColorSaturationPowerAndScale(LightColorAndAttenuation, View.MooaLightSaturationScale);
/* Tips: /* Tips:
* SvPosition == PixelPos (0 ~ View.BufferSizeAndInvSize.xy) * SceneTextureUV == BufferUV == ScreenUV (0 ~ 1), used to GetScreenSpaceData() / GetGBufferData() * * ScreenPosition = ClipPosition.xy / ClipPosition.w * SvPosition == PixelPos (0 ~ View.BufferSizeAndInvSize.xy)
* 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); * 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 float3 H = normalize(V + L);
const float NoH = saturate(dot(N, H)); const float2 BufferUV = SvPositionToBufferUV(float4(MooaToonContext.PixelPos, 0, 0));
const float halfNoL = dot(N, L) * 0.5f + 0.5f; const float2 ViewportUV = BufferUVToViewportUV(BufferUV);
const float2 BufferUV = SvPositionToBufferUV(float4(Context.PixelPos, 0, 0));
const float3 L_ClipSpace = mul(L, (float3x3)View.TranslatedWorldToClip).xyz; const float3 L_ClipSpace = mul(L, (float3x3)View.TranslatedWorldToClip).xyz;
const float2 L_ViewportSpace = normalize(L_ClipSpace.xy * float2(0.5, -0.5)); const float2 L_ViewportSpace = (L_ClipSpace.xy * float2(0.5, -0.5));
const float3 N_ClipSpace = mul(N, (float3x3)View.TranslatedWorldToClip).xyz; const float3 N_ClipSpace = mul(N, (float3x3)View.TranslatedWorldToClip).xyz;
const float2 N_ViewportSpace = normalize(N_ClipSpace.xy * float2(0.5, -0.5)); const float2 N_ViewportSpace = (N_ClipSpace.xy * float2(0.5, -0.5));
const float WorldUnitLengthInBufferSizePixels = ComputeWorldUnitLengthInBufferSizePixels(GBuffer.Depth); const float ViewportSpaceToWorldSpaceDir = rcp(GBuffer.Depth);
// Diffuse // Diffuse
BRANCH if (Context.IsMainLight) {
{ // Screen Space Depth Test Hair Shadow float HairShadowOffset = 0;
float HairShadowValueOffset = 0; float DiffuseColorRampUVOffset = (ToonGBuffer.DiffuseColorRampUVOffset * 2.0f - 1.0f) * View.MooaDiffuseColorRampUVOffsetMaxRange;
BRANCH if(MooaToonData.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_FACE_SCREEN_SPACE_HAIR_SHADOW && float ShadowGradient = saturate(NoL_Half + DiffuseColorRampUVOffset);
MooaToonData.HairShadowWidth > 0 && MooaToonData.HairShadowIntensity > 0)
{ float2 UVOffset = L_ViewportSpace * WorldUnitLengthInBufferSizePixels * Pow2(MooaToonData.HairShadowWidth) * View.MooaHairShadowMaxScreenSpaceDistance; #if SHADING_PATH_DEFERRED && defined(MOOA_TOON_DEFERRED_LIGHTING) && !SUBSTRATE_ENABLED
FGBufferData HairGbuffer = GetGBufferData(BufferUV + UVOffset); {
float DepthFade = 1 - saturate(max(0, HairGbuffer.Depth - GBuffer.Depth - View.MooaHairShadowDepthTestThreshold) / max(1e-5, View.MooaHairShadowDepthFadeDistance)); // 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 && if (HairGbuffer.ShadingModelID == SHADINGMODELID_TOON &&
HairGbuffer.MooaToonContext.MooaToonData.RayTracingShadowFlag == MOOA_RAY_TRACING_SHADOW_FLAG_HAIR) HairGbuffer.MooaToonContext.ToonGBuffer.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); HairShadowOffset = -1 * HairShadowIntensity * DepthFade;
// 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 #endif
BxDFContext AnisotropyContext = (BxDFContext)0;
{ half3 X = GBuffer.WorldTangent; 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)); half3 Y = normalize(cross(N, X));
Init(AnisotropyContext, N, X, Y, V, L); Init(Context, N, X, Y, V, L);
AnisotropyContext.NoV = saturate(abs( AnisotropyContext.NoV ) + 1e-5);
if (!bHasAnisotropy) GBuffer.Anisotropy = 0;
} }
// Specular else
BRANCH if (MooaToonData.IsPBRSpecular)
{ Lighting.Specular = LightColorAndAttenuation * NoL *
max(0, SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, AnisotropyContext, NoL, AreaLight));
} else
{ {
// Anisotropy Init(Context, N, V, L);
float2 XY = normalize(float2(1.0f + GBuffer.Anisotropy, 1.0f - GBuffer.Anisotropy)); GBuffer.Anisotropy = 0;
float AnisotropyGradient = length(float2(XY.y * AnisotropyContext.XoH, XY.x * AnisotropyContext.YoH)); }
// Toon Specular: https://www.desmos.com/calculator/qecziyizl1 Context.NoV = saturate(abs( Context.NoV ) + 1e-5);
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 // Specular
// TranslucentBasePass.SceneDepth include Translucent First Layer Depth BRANCH if (ToonGBuffer.ShadingFeatureID == MOOA_SHADING_FEATURE_ID_PBR_SPECULAR)
// see UseFrontLayerReflection() float SingleLayerDeviceZ = Texture2DSampleLevel( TranslucentBasePass.SceneDepth, GlobalPointClampedSampler, TargetBufferUV, 0).r; {
float NewDepth = ConvertFromDeviceZ(SingleLayerDeviceZ); #if SUPPORTS_ANISOTROPIC_MATERIALS
#else BRANCH if (bHasAnisotropy)
float NewDepth = GetGBufferData(TargetBufferUV).Depth; {
Lighting.Specular = LightColorAndAttenuation * Shadow.SurfaceShadow * NoL * SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, Context, NoL, AreaLight);
}
else
#endif #endif
float DepthFade = saturate(max(0, NewDepth - GBuffer.Depth - MaxDepthTestDistance * Pow2(MooaToonData.RimLightDepthThreshold)) {
/ max(1e-5, MaxDepthFadeDistance * Pow2(MooaToonData.RimLightDepthThreshold))); BRANCH if( IsRectLight(AreaLight) )
Lighting.Specular = max(Lighting.Specular, DepthFade * MooaToonData.RimLightIntensity * LightColorAndAttenuation); {
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; return Lighting;
} }
``` ```
# 实用函数
如果需要脸部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);
}
}
```