3D SDF 多光源 阴影 的不同尝试

发布时间:2026/6/10 18:41:54

3D SDF 多光源 阴影 的不同尝试 一、选择对角色光照贡献最大的一盏灯作为主灯用于主基调的亮光面和阴影面其他灯光对亮光面和被光面进行补光增亮如果对角色模型在所有光源里面进行遍历找到对角色光照贡献最强的一盏光作为主光那么当角色移动或者有一盏灯亮度逐渐增大时取代之前的主光容易产生阴影闪烁的问题例如主灯会硬切如果当前主灯 A 强度是 10副灯 B 慢慢变到 10.1材质里如果直接选最大值主灯会瞬间从 A 切到 B。那么现在问题就是如何平和过渡这个主光的切换二、根据每盏灯对角色脸部光照贡献程度取一个带权重的方向作为主光且只有一个主光没有副光做法就是每个方向都进行带权加到一个方向向量上比如说我这里离角色近且灯光亮度高那么我就对最后的主方向给的贡献就更大这样带权累加过后进行归一化处理这样的好处我就不需要再考虑什么主光源之间的切换问题不会有单体上的主光源的概念只会灯光根据对角色脸部的光照贡献对最终方向进行加权多个方向会生成不存在的阴影形状比如左前方一盏灯、右前方一盏灯。如果你把两个方向加权平均得到的是“正前方光”。但真实卡渲里这不一定等价因为 SDF 图集里“正前方阴影”是单独画出来的美术结果不是左右两张图数学平均出来的。三、我选择一个灯光作为主光并一直保持不变例如选一盏方向光做法就是选一个灯光作为主光不管其他灯光对角色脸部亮度的贡献是否大于选择的这盏主光都不会代替人为选择的这个主光其他的灯光对脸部进行补光好处不会有不正常的阴影或者主光发生变化产生的阴影突变的问题坏处加入我突然进屋了那么按照道理这个方向光对角色的贡献已经失效了我应该选一个新主光那么这个时候假如选择了新主光那么阴影很容易造成阴影突变的问题这其实本质又是主光发生突变导致的阴影突变的问题四、我根据光照对脸部的光照贡献选最强光为主光其余光如果能够照到主光的阴影区则增亮阴影区若光照贡献相同则脸部两盏灯照的是一样亮的特别注意一点就是照亮阴影不能只照亮一部分那样会特别脏如果其余光照亮了主光阴影的全部范围这样才对阴影提亮进行贡献否则不贡献如果只是这样去限制会产生一个情况就是我现在有一个主光和多个副光我任一一个副光对脸部的光照着色不超过主光但是我可能多个副光对主光阴影的贡献会超过主光这个情况那我该如何去限制还有一个问题我们刚才只讨论了标题上面是根据一盏灯在角色左前方一盏灯在右前方互相照亮对方的阴影提亮彼此的阴影的情况我们还有一个情况没有考虑到那就是两盏灯同侧同为角色向前左前方同为角色向前右前方但是角度不同我就不应该存在完全照亮对方阴影的可能所以这个时候我尝试用smooth union平滑融合角度比较小的阴影使得当同一侧的A主光被同一侧的B副光替代为新主光的时候有一个很平滑的阴影融合效果性能优化方面我希望对脸部光照贡献最强的和第二强的才采样SDF贴图其余直接根据光照贡献对脸部进行增亮即可不用采样还有一个就是当对脸部光照贡献小于某个值的时候我将剩下的逻辑直接跳过continue不进行下一步更复杂的计算代码如下// M_qita_SDF_3D Custom HLSL // Key light controls main 3D SDF shadow. // Top1/Top2 relation drives stable transition. // Same-side close Top2 smooth-unions with key shadow. // Opposite-side close Top2 fades key shadow to lit. // Other fills only brighten, they do not reshape the main shadow. // Exposure is allowed to output values above 1.0. #ifndef GM_BASEPASS_FORWARD_LIGHT_ACCESS #define GM_BASEPASS_FORWARD_LIGHT_ACCESS 0 #endif #define FACE_FORWARD_AXIS float3(0.0, 1.0, 0.0) #define FACE_RIGHT_AXIS float3(1.0, 0.0, 0.0) #define FACE_UP_AXIS float3(0.0, 0.0, 1.0) #define FACE_CENTER_OFFSET_LOCAL float3(0.0, 0.0, 145.0) #define SDF_ATLAS_SIZE 9.0 #define SDF_ATLAS_MAX_CELL 8.0 #define SDF_CELL_UV_PADDING 0.008 #define SDF_EDGE_WIDTH 0.08 #define SDF_SHADOW_MIN 0.22 #define SDF_LIGHT_POWER 1.0 #define SDF_NO_SHADOW_VALUE 1.0 #define LOCAL_LIGHT_VISUAL_REFERENCE_INTENSITY 80.0 #define DIRECTIONAL_LIGHT_VISUAL_REFERENCE_INTENSITY 1.0 #define QITA_DIRECTIONAL_VISUAL_INTENSITY_FIX 3.14159265 #define SDF_LOCAL_VISUAL_RESPONSE_SCALE 0.005 #define SDF_LOCAL_KEY_WEIGHT 1.0 #define SDF_DIRECTIONAL_KEY_WEIGHT 1.0 #define SDF_FILL_SHADOW_STRENGTH 1.0 #define SDF_FILL_RATIO_MAX 1.0 #define SDF_FILL_VISUAL_SCALE 1.0 #define SDF_FILL_FACE_GATE_START -0.20 #define SDF_FILL_FACE_GATE_END 0.30 #define SDF_FILL_SHADOW_SIDE_START 0.15 #define SDF_FILL_SHADOW_SIDE_END 0.80 #define SDF_SAME_SIDE_RAW_START -0.08 #define SDF_SAME_SIDE_RAW_END 0.08 #define SDF_SAME_SIDE_CONTRIB_START 0.70 #define SDF_SAME_SIDE_CONTRIB_END 0.95 #define SDF_SAME_SIDE_SMOOTH_UNION_K 0.30 #define SDF_CROSS_SIDE_FADE_START 0.70 #define SDF_CROSS_SIDE_FADE_END 1.0 #define SDF_VISUAL_COLOR_SCALE 0.18 #define SDF_VISUAL_COLOR_MAX 0.65 #define SDF_VISUAL_LIT_START 0.35 #define SDF_VISUAL_LIT_END 1.0 #define SDF_EXPOSURE_SCALE 0.035 #define SDF_EXPOSURE_LIT_START 0.62 #define SDF_EXPOSURE_LIT_END 0.98 #define SDF_EXPOSURE_LIT_POWER 1.40 #define SDF_VERTICAL_BLEND_START 0.04 #define SDF_VERTICAL_BLEND_END 0.30 #define SDF_FRONT_BACK_BLEND_WIDTH 0.10 #define SDF_LR_BLEND_WIDTH 0.04 #define SDF_LR_SIGN_FLIP 1.0 #define QITA_SAFE_NORM3(V) ((V) * rsqrt(max(dot((V), (V)), 0.0001))) #define QITA_SAMPLE_SDF3D(UV_VALUE) Texture2DSample(SDF3D, SDF3DSampler, UV_VALUE).r #define QITA_TO_SDF_MASK(SIGNED_SDF_VALUE) pow(saturate(smoothstep(-SDF_EDGE_WIDTH, SDF_EDGE_WIDTH, SIGNED_SDF_VALUE)), SDF_LIGHT_POWER) #define QITA_SMOOTH_MAX_SIGNED(A_VALUE, B_VALUE, K_VALUE, OUT_VALUE) \ { \ float _SmoothA (A_VALUE); \ float _SmoothB (B_VALUE); \ float _SmoothK max((K_VALUE), 0.0001); \ float _SmoothH saturate(0.5 0.5 * (_SmoothA - _SmoothB) / _SmoothK); \ OUT_VALUE clamp(lerp(_SmoothB, _SmoothA, _SmoothH) _SmoothK * _SmoothH * (1.0 - _SmoothH), -1.0, 1.0); \ } float4x4 LocalToWorld DFHackToFloat(GetPrimitiveData(Parameters).LocalToWorld); float3 ObjectTranslatedWorld GetObjectTranslatedWorldPosition(Parameters); float3 FaceForwardWS QITA_SAFE_NORM3(mul(float4(FACE_FORWARD_AXIS, 0.0), LocalToWorld).xyz); float3 FaceRightWS QITA_SAFE_NORM3(mul(float4(FACE_RIGHT_AXIS, 0.0), LocalToWorld).xyz); float3 FaceUpWS QITA_SAFE_NORM3(mul(float4(FACE_UP_AXIS, 0.0), LocalToWorld).xyz); float3 FaceCenterWS ObjectTranslatedWorld mul(float4(FACE_CENTER_OFFSET_LOCAL, 0.0), LocalToWorld).xyz; float2 TexCoord UV0; float3 BaseColor Texture2DSample(ColorTexture, ColorTextureSampler, UV0).rgb; float KeyScore 0.0; float3 KeyLightDir FaceForwardWS; float KeyVisualEnergy 0.0; float KeyIsDirectional 0.0; uint KeyLocalLightIndex 0; float SecondScore 0.0; float3 SecondLightDir FaceForwardWS; float SecondVisualEnergy 0.0; float SecondIsDirectional 0.0; uint SecondLocalLightIndex 0; float KeyMask 0.0; float OriginalKeyMask 0.0; float FusedKeySignedSDF SDF_NO_SHADOW_VALUE; float FillShadowAccum 0.0; float CrossSideFadeAccum 0.0; float PixelVisualEnergy 0.0; #define QITA_PUSH_TOP2(CAND_SCORE, CAND_DIR, CAND_VISUAL, CAND_IS_DIRECTIONAL, CAND_INDEX) \ { \ float _CandidateScore (CAND_SCORE); \ if (_CandidateScore KeyScore) \ { \ SecondScore KeyScore; \ SecondLightDir KeyLightDir; \ SecondVisualEnergy KeyVisualEnergy; \ SecondIsDirectional KeyIsDirectional; \ SecondLocalLightIndex KeyLocalLightIndex; \ KeyScore _CandidateScore; \ KeyLightDir (CAND_DIR); \ KeyVisualEnergy (CAND_VISUAL); \ KeyIsDirectional (CAND_IS_DIRECTIONAL); \ KeyLocalLightIndex (CAND_INDEX); \ } \ else if (_CandidateScore SecondScore) \ { \ SecondScore _CandidateScore; \ SecondLightDir (CAND_DIR); \ SecondVisualEnergy (CAND_VISUAL); \ SecondIsDirectional (CAND_IS_DIRECTIONAL); \ SecondLocalLightIndex (CAND_INDEX); \ } \ } #define QITA_SAMPLE_ATLAS_BY_FACE_DOTS(DOT_FORWARD_VALUE, SIDE_SIGN_VALUE, DOT_UP_VALUE, OUT_SIGNED_SDF) \ { \ float _MirrorWeight smoothstep(-SDF_LR_BLEND_WIDTH, SDF_LR_BLEND_WIDTH, (SIDE_SIGN_VALUE) * SDF_LR_SIGN_FLIP); \ float2 _RawCellUV saturate(TexCoord); \ float _CellU_A lerp(SDF_CELL_UV_PADDING, 1.0 - SDF_CELL_UV_PADDING, _RawCellUV.x); \ float _CellU_B lerp(SDF_CELL_UV_PADDING, 1.0 - SDF_CELL_UV_PADDING, 1.0 - _RawCellUV.x); \ float _CellV lerp(SDF_CELL_UV_PADDING, 1.0 - SDF_CELL_UV_PADDING, _RawCellUV.y); \ float2 _CellUV_A float2(_CellU_A, _CellV); \ float2 _CellUV_B float2(_CellU_B, _CellV); \ float2 _Angle01 1.0 - acos(clamp(float2(DOT_FORWARD_VALUE, DOT_UP_VALUE), -1.0, 1.0)) * 0.31830988618; \ float2 _Grid min(floor(_Angle01 * SDF_ATLAS_MAX_CELL), float2(SDF_ATLAS_MAX_CELL, SDF_ATLAS_MAX_CELL)); \ float2 _Frac frac(_Angle01 * SDF_ATLAS_MAX_CELL); \ float2 _G00 _Grid; \ float2 _G10 min(_Grid float2(1.0, 0.0), float2(SDF_ATLAS_MAX_CELL, SDF_ATLAS_MAX_CELL)); \ float2 _G01 min(_Grid float2(0.0, 1.0), float2(SDF_ATLAS_MAX_CELL, SDF_ATLAS_MAX_CELL)); \ float2 _G11 min(_Grid float2(1.0, 1.0), float2(SDF_ATLAS_MAX_CELL, SDF_ATLAS_MAX_CELL)); \ float _S00A QITA_SAMPLE_SDF3D((_G00 _CellUV_A) / SDF_ATLAS_SIZE); \ float _S10A QITA_SAMPLE_SDF3D((_G10 _CellUV_A) / SDF_ATLAS_SIZE); \ float _S01A QITA_SAMPLE_SDF3D((_G01 _CellUV_A) / SDF_ATLAS_SIZE); \ float _S11A QITA_SAMPLE_SDF3D((_G11 _CellUV_A) / SDF_ATLAS_SIZE); \ float _SampleA lerp(lerp(_S00A, _S10A, _Frac.x), lerp(_S01A, _S11A, _Frac.x), _Frac.y); \ float _S00B QITA_SAMPLE_SDF3D((_G00 _CellUV_B) / SDF_ATLAS_SIZE); \ float _S10B QITA_SAMPLE_SDF3D((_G10 _CellUV_B) / SDF_ATLAS_SIZE); \ float _S01B QITA_SAMPLE_SDF3D((_G01 _CellUV_B) / SDF_ATLAS_SIZE); \ float _S11B QITA_SAMPLE_SDF3D((_G11 _CellUV_B) / SDF_ATLAS_SIZE); \ float _SampleB lerp(lerp(_S00B, _S10B, _Frac.x), lerp(_S01B, _S11B, _Frac.x), _Frac.y); \ float _SDFSample lerp(_SampleA, _SampleB, _MirrorWeight); \ OUT_SIGNED_SDF clamp((0.5 - _SDFSample) * 2.0, -1.0, 1.0); \ } #define QITA_SAMPLE_3D_FACE_SDF_SIGNED(LIGHT_DIR_VALUE, OUT_SIGNED_SDF) \ { \ float3 _LightDir QITA_SAFE_NORM3(LIGHT_DIR_VALUE); \ float _UpRaw dot(FaceUpWS, _LightDir); \ float3 _Planar _LightDir - FaceUpWS * _UpRaw; \ float _HorizontalLen length(_Planar); \ float3 _PlanarDir _Planar * rsqrt(max(dot(_Planar, _Planar), 0.0001)); \ float _NoHorizontal 1.0 - step(0.0001, _HorizontalLen); \ _PlanarDir lerp(_PlanarDir, FaceForwardWS, _NoHorizontal); \ float _DotForward dot(FaceForwardWS, _PlanarDir); \ float _SideSign dot(FaceRightWS, _PlanarDir); \ float _DotUp -_UpRaw; \ float _NormalSigned SDF_NO_SHADOW_VALUE; \ QITA_SAMPLE_ATLAS_BY_FACE_DOTS(_DotForward, _SideSign, _DotUp, _NormalSigned); \ float _FrontSign _DotForward 0.0 ? 1.0 : -1.0; \ float _BeforeDotForward abs(_DotForward); \ float _BeforeSideSign _SideSign * _FrontSign; \ float _AfterDotForward -_BeforeDotForward; \ float _AfterSideSign -_BeforeSideSign; \ float _Before90Signed SDF_NO_SHADOW_VALUE; \ float _After90Signed SDF_NO_SHADOW_VALUE; \ QITA_SAMPLE_ATLAS_BY_FACE_DOTS(_BeforeDotForward, _BeforeSideSign, _DotUp, _Before90Signed); \ QITA_SAMPLE_ATLAS_BY_FACE_DOTS(_AfterDotForward, _AfterSideSign, _DotUp, _After90Signed); \ float _Forward3D dot(FaceForwardWS, _LightDir); \ float _Before90Weight smoothstep(-SDF_FRONT_BACK_BLEND_WIDTH, SDF_FRONT_BACK_BLEND_WIDTH, _Forward3D); \ float _DualSigned lerp(_After90Signed, _Before90Signed, _Before90Weight); \ float _VerticalBlend 1.0 - smoothstep(SDF_VERTICAL_BLEND_START, SDF_VERTICAL_BLEND_END, _HorizontalLen); \ OUT_SIGNED_SDF lerp(_NormalSigned, _DualSigned, _VerticalBlend); \ } #if GM_BASEPASS_FORWARD_LIGHT_ACCESS FEATURE_LEVEL FEATURE_LEVEL_SM5 const uint EyeIndex GM_GetBasePassForwardEyeIndex(Parameters); const uint GridIndex GM_GetForwardLightGridIndex(Parameters); const FCulledLightsGridHeader Header GetCulledLightsGridHeader(GridIndex); const uint NumLights min(Header.NumLights, GetMaxLightsPerCell()); const FDirectionalLightData DirectionalLightData GetDirectionalLightData(); float3 DirectionalLightDirStored FaceForwardWS; float DirectionalVisualEnergyStored 0.0; float DirectionalScoreStored 0.0; if (DirectionalLightData.HasDirectionalLight ! 0) { DirectionalLightDirStored QITA_SAFE_NORM3(DirectionalLightData.DirectionalLightDirection); float DirectionalRawIntensity dot(DirectionalLightData.DirectionalLightColor, float3(0.299, 0.587, 0.114)); DirectionalVisualEnergyStored max((DirectionalRawIntensity * QITA_DIRECTIONAL_VISUAL_INTENSITY_FIX) / max(DIRECTIONAL_LIGHT_VISUAL_REFERENCE_INTENSITY, 0.001), 0.0); DirectionalScoreStored DirectionalVisualEnergyStored * SDF_DIRECTIONAL_KEY_WEIGHT; QITA_PUSH_TOP2(DirectionalScoreStored, DirectionalLightDirStored, DirectionalVisualEnergyStored, 1.0, 0); } LOOP for (uint LightIndex 0; LightIndex NumLights; LightIndex) { const FLocalLightData LocalLight GetLocalLightDataFromGrid(Header.DataStartIndex LightIndex, EyeIndex); float3 LightPos UnpackLightTranslatedWorldPosition(LocalLight); float3 FaceToLight LightPos - FaceCenterWS; float FaceDistSqr dot(FaceToLight, FaceToLight); float3 LightDir QITA_SAFE_NORM3(FaceToLight); float InvRadius UnpackLightInvRadius(LocalLight); float Dist01Sqr FaceDistSqr * InvRadius * InvRadius; float Fade saturate(1.0 - Dist01Sqr * Dist01Sqr); float Atten Fade * Fade * Fade; float3 LightColor UnpackLightColor(LocalLight); float RawIntensity dot(LightColor, float3(0.299, 0.587, 0.114)); float VisualEnergy Atten * max(RawIntensity / max(LOCAL_LIGHT_VISUAL_REFERENCE_INTENSITY, 0.001), 0.0) * SDF_LOCAL_VISUAL_RESPONSE_SCALE; float LocalScore VisualEnergy * SDF_LOCAL_KEY_WEIGHT; QITA_PUSH_TOP2(LocalScore, LightDir, VisualEnergy, 0.0, LightIndex); } if (KeyScore 0.000001) { float KeySignedSDF SDF_NO_SHADOW_VALUE; QITA_SAMPLE_3D_FACE_SDF_SIGNED(KeyLightDir, KeySignedSDF); OriginalKeyMask QITA_TO_SDF_MASK(KeySignedSDF); FusedKeySignedSDF KeySignedSDF; if (SecondScore 0.000001) { float3 SecondDirN QITA_SAFE_NORM3(SecondLightDir); float3 KeyDirN QITA_SAFE_NORM3(KeyLightDir); float SecondRatio min(SecondScore / max(KeyScore, 0.0001), SDF_FILL_RATIO_MAX); float SameSideRaw dot(FaceRightWS, SecondDirN) * dot(FaceRightWS, KeyDirN); float SameSideGate smoothstep(SDF_SAME_SIDE_RAW_START, SDF_SAME_SIDE_RAW_END, SameSideRaw); float CrossSideGate 1.0 - SameSideGate; float SameSideContribGate smoothstep(SDF_SAME_SIDE_CONTRIB_START, SDF_SAME_SIDE_CONTRIB_END, SecondRatio); float SameSideFuseWeight saturate(SameSideGate * SameSideContribGate); if (SameSideFuseWeight 0.0001) { float SecondSignedSDF SDF_NO_SHADOW_VALUE; QITA_SAMPLE_3D_FACE_SDF_SIGNED(SecondDirN, SecondSignedSDF); float WeightedSecondSignedSDF lerp(-SDF_NO_SHADOW_VALUE, SecondSignedSDF, SameSideFuseWeight); QITA_SMOOTH_MAX_SIGNED(FusedKeySignedSDF, WeightedSecondSignedSDF, SDF_SAME_SIDE_SMOOTH_UNION_K, FusedKeySignedSDF); } float CrossFade CrossSideGate * smoothstep(SDF_CROSS_SIDE_FADE_START, SDF_CROSS_SIDE_FADE_END, SecondRatio); CrossSideFadeAccum max(CrossSideFadeAccum, CrossFade); float SecondFaceGate smoothstep(SDF_FILL_FACE_GATE_START, SDF_FILL_FACE_GATE_END, dot(FaceForwardWS, SecondDirN)); float SecondShadowSide 1.0 - smoothstep(SDF_FILL_SHADOW_SIDE_START, SDF_FILL_SHADOW_SIDE_END, dot(SecondDirN, KeyDirN)); float SecondGlobalMask saturate(SecondFaceGate * SecondShadowSide * CrossSideGate); FillShadowAccum SecondGlobalMask * SecondRatio * SDF_FILL_SHADOW_STRENGTH; float SecondVisualMask saturate(OriginalKeyMask * SecondFaceGate (1.0 - OriginalKeyMask) * max(SecondGlobalMask, SameSideFuseWeight * SecondFaceGate CrossFade)); PixelVisualEnergy SecondVisualEnergy * SecondVisualMask * SDF_FILL_VISUAL_SCALE; } if (DirectionalLightData.HasDirectionalLight ! 0 KeyIsDirectional 0.5 SecondIsDirectional 0.5) { float3 FillDirN QITA_SAFE_NORM3(DirectionalLightDirStored); float3 KeyDirN QITA_SAFE_NORM3(KeyLightDir); float FillRatio min(DirectionalScoreStored / max(KeyScore, 0.0001), SDF_FILL_RATIO_MAX); float SameSideRaw dot(FaceRightWS, FillDirN) * dot(FaceRightWS, KeyDirN); float SameSideGate smoothstep(SDF_SAME_SIDE_RAW_START, SDF_SAME_SIDE_RAW_END, SameSideRaw); float CrossSideGate 1.0 - SameSideGate; float FillFaceGate smoothstep(SDF_FILL_FACE_GATE_START, SDF_FILL_FACE_GATE_END, dot(FaceForwardWS, FillDirN)); float FillShadowSide 1.0 - smoothstep(SDF_FILL_SHADOW_SIDE_START, SDF_FILL_SHADOW_SIDE_END, dot(FillDirN, KeyDirN)); float FillGlobalMask saturate(FillFaceGate * FillShadowSide * CrossSideGate); FillShadowAccum FillGlobalMask * FillRatio * SDF_FILL_SHADOW_STRENGTH; float FillVisualMask saturate(OriginalKeyMask * FillFaceGate (1.0 - OriginalKeyMask) * FillGlobalMask); PixelVisualEnergy DirectionalVisualEnergyStored * FillVisualMask * SDF_FILL_VISUAL_SCALE; } LOOP for (uint LightIndex 0; LightIndex NumLights; LightIndex) { bool UsedAsKey (KeyIsDirectional 0.5 LightIndex KeyLocalLightIndex); bool UsedAsSecond (SecondScore 0.000001 SecondIsDirectional 0.5 LightIndex SecondLocalLightIndex); if (!UsedAsKey !UsedAsSecond) { const FLocalLightData LocalLight GetLocalLightDataFromGrid(Header.DataStartIndex LightIndex, EyeIndex); float3 LightPos UnpackLightTranslatedWorldPosition(LocalLight); float3 FaceToLight LightPos - FaceCenterWS; float FaceDistSqr dot(FaceToLight, FaceToLight); float3 LightDir QITA_SAFE_NORM3(FaceToLight); float InvRadius UnpackLightInvRadius(LocalLight); float Dist01Sqr FaceDistSqr * InvRadius * InvRadius; float Fade saturate(1.0 - Dist01Sqr * Dist01Sqr); float Atten Fade * Fade * Fade; float3 LightColor UnpackLightColor(LocalLight); float RawIntensity dot(LightColor, float3(0.299, 0.587, 0.114)); float VisualEnergy Atten * max(RawIntensity / max(LOCAL_LIGHT_VISUAL_REFERENCE_INTENSITY, 0.001), 0.0) * SDF_LOCAL_VISUAL_RESPONSE_SCALE; float LocalScore VisualEnergy * SDF_LOCAL_KEY_WEIGHT; float3 FillDirN QITA_SAFE_NORM3(LightDir); float3 KeyDirN QITA_SAFE_NORM3(KeyLightDir); float FillRatio min(LocalScore / max(KeyScore, 0.0001), SDF_FILL_RATIO_MAX); float SameSideRaw dot(FaceRightWS, FillDirN) * dot(FaceRightWS, KeyDirN); float SameSideGate smoothstep(SDF_SAME_SIDE_RAW_START, SDF_SAME_SIDE_RAW_END, SameSideRaw); float CrossSideGate 1.0 - SameSideGate; float FillFaceGate smoothstep(SDF_FILL_FACE_GATE_START, SDF_FILL_FACE_GATE_END, dot(FaceForwardWS, FillDirN)); float FillShadowSide 1.0 - smoothstep(SDF_FILL_SHADOW_SIDE_START, SDF_FILL_SHADOW_SIDE_END, dot(FillDirN, KeyDirN)); float FillGlobalMask saturate(FillFaceGate * FillShadowSide * CrossSideGate); FillShadowAccum FillGlobalMask * FillRatio * SDF_FILL_SHADOW_STRENGTH; float FillVisualMask saturate(OriginalKeyMask * FillFaceGate (1.0 - OriginalKeyMask) * FillGlobalMask); PixelVisualEnergy VisualEnergy * FillVisualMask * SDF_FILL_VISUAL_SCALE; } } KeyMask QITA_TO_SDF_MASK(FusedKeySignedSDF); KeyMask lerp(KeyMask, 1.0, saturate(CrossSideFadeAccum)); PixelVisualEnergy KeyVisualEnergy * KeyMask; } #endif float FillShadowMask saturate(FillShadowAccum); float SDFMask saturate(KeyMask (1.0 - KeyMask) * FillShadowMask); float FinalMask lerp(SDF_SHADOW_MIN, 1.0, SDFMask); float VisualLitMask smoothstep(SDF_VISUAL_LIT_START, SDF_VISUAL_LIT_END, SDFMask); float VisualBoost min(max(PixelVisualEnergy * SDF_VISUAL_COLOR_SCALE, 0.0), SDF_VISUAL_COLOR_MAX); FinalMask lerp(FinalMask, 1.0, VisualBoost * VisualLitMask); float3 FinalTint lerp(ShadowColor, NormalColor, FinalMask); float3 ResultColor FinalTint * BaseColor; float ExposureMask smoothstep(SDF_EXPOSURE_LIT_START, SDF_EXPOSURE_LIT_END, SDFMask); ExposureMask pow(saturate(ExposureMask), SDF_EXPOSURE_LIT_POWER); float ExposureBoost max(PixelVisualEnergy * SDF_EXPOSURE_SCALE, 0.0); ResultColor (NormalColor * BaseColor) * ExposureBoost * ExposureMask; #undef QITA_PUSH_TOP2 #undef QITA_SMOOTH_MAX_SIGNED #undef QITA_TO_SDF_MASK #undef QITA_SAMPLE_3D_FACE_SDF_SIGNED #undef QITA_SAMPLE_ATLAS_BY_FACE_DOTS #undef QITA_SAMPLE_SDF3D #undef QITA_SAFE_NORM3 #undef FACE_FORWARD_AXIS #undef FACE_RIGHT_AXIS #undef FACE_UP_AXIS #undef FACE_CENTER_OFFSET_LOCAL #undef SDF_ATLAS_SIZE #undef SDF_ATLAS_MAX_CELL #undef SDF_CELL_UV_PADDING #undef SDF_EDGE_WIDTH #undef SDF_SHADOW_MIN #undef SDF_LIGHT_POWER #undef SDF_NO_SHADOW_VALUE #undef LOCAL_LIGHT_VISUAL_REFERENCE_INTENSITY #undef DIRECTIONAL_LIGHT_VISUAL_REFERENCE_INTENSITY #undef QITA_DIRECTIONAL_VISUAL_INTENSITY_FIX #undef SDF_LOCAL_VISUAL_RESPONSE_SCALE #undef SDF_LOCAL_KEY_WEIGHT #undef SDF_DIRECTIONAL_KEY_WEIGHT #undef SDF_FILL_SHADOW_STRENGTH #undef SDF_FILL_RATIO_MAX #undef SDF_FILL_VISUAL_SCALE #undef SDF_FILL_FACE_GATE_START #undef SDF_FILL_FACE_GATE_END #undef SDF_FILL_SHADOW_SIDE_START #undef SDF_FILL_SHADOW_SIDE_END #undef SDF_SAME_SIDE_RAW_START #undef SDF_SAME_SIDE_RAW_END #undef SDF_SAME_SIDE_CONTRIB_START #undef SDF_SAME_SIDE_CONTRIB_END #undef SDF_SAME_SIDE_SMOOTH_UNION_K #undef SDF_CROSS_SIDE_FADE_START #undef SDF_CROSS_SIDE_FADE_END #undef SDF_VISUAL_COLOR_SCALE #undef SDF_VISUAL_COLOR_MAX #undef SDF_VISUAL_LIT_START #undef SDF_VISUAL_LIT_END #undef SDF_EXPOSURE_SCALE #undef SDF_EXPOSURE_LIT_START #undef SDF_EXPOSURE_LIT_END #undef SDF_EXPOSURE_LIT_POWER #undef SDF_VERTICAL_BLEND_START #undef SDF_VERTICAL_BLEND_END #undef SDF_FRONT_BACK_BLEND_WIDTH #undef SDF_LR_BLEND_WIDTH #undef SDF_LR_SIGN_FLIP return ResultColor;

相关新闻