From 8e9daf771727e9393f592908b22faddef6de5875 Mon Sep 17 00:00:00 2001 From: BlueRose <378100977@qq.com> Date: Thu, 6 Feb 2025 21:31:49 +0800 Subject: [PATCH] vault backup: 2025-02-06 21:31:49 --- .../演讲笔记/JasonMa/JasonMa渲染方案分析.md | 270 +++++++++++------- 1 file changed, 163 insertions(+), 107 deletions(-) diff --git a/03-UnrealEngine/卡通渲染相关资料/演讲笔记/JasonMa/JasonMa渲染方案分析.md b/03-UnrealEngine/卡通渲染相关资料/演讲笔记/JasonMa/JasonMa渲染方案分析.md index 7f098fd..a3ad5ee 100644 --- a/03-UnrealEngine/卡通渲染相关资料/演讲笔记/JasonMa/JasonMa渲染方案分析.md +++ b/03-UnrealEngine/卡通渲染相关资料/演讲笔记/JasonMa/JasonMa渲染方案分析.md @@ -311,111 +311,167 @@ else ``` ```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; + +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; } -``` \ No newline at end of file + +``` + +# 实用函数 +如果需要脸部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); + } +} +``` + +