Files
UnrealEngine/Engine/Shaders/Private/ShadowProjectionPixelShader.usf
2025-05-18 13:04:45 +08:00

556 lines
21 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShadowProjectionPixelShader.usf: Pixel shader for projecting a shadow depth buffer onto the scene.
=============================================================================*/
#ifndef USE_FADE_PLANE
#define USE_FADE_PLANE 0
#endif
#ifndef SHADOW_QUALITY
#define SHADOW_QUALITY 6
#endif
#ifndef APPLY_TRANSLUCENCY_SHADOWS
#define APPLY_TRANSLUCENCY_SHADOWS 0
#endif
#ifndef USE_PCSS
#define USE_PCSS 0
#endif
#ifndef SPOT_LIGHT_PCSS
#define SPOT_LIGHT_PCSS 0
#endif
#include "HairStrands/HairStrandsVisibilityCommonStruct.ush"
#include "Common.ush"
// Needs to bind MobileBasePass UB since the modulated shadow is in mobile base pass.
#if MODULATED_SHADOWS
#define MobileSceneTextures MobileBasePass.SceneTextures
#endif
#if SHADING_PATH_MOBILE
#include "MobileLightingCommon.ush"
#endif
#include "ShadowProjectionCommon.ush"
#include "ShadowFilteringCommon.ush"
#if USE_PCSS
#include "ShadowPercentageCloserFiltering.ush"
#endif
#include "DeferredShadingCommon.ush"
#include "DynamicLightingCommon.ush"
float ShadowFadeFraction;
float ShadowSharpen;
float4 LightPositionAndInvRadius;
float PerObjectShadowFadeStart;
float InvPerObjectShadowFadeLength;
#if USE_TRANSMISSION
#include "TransmissionThickness.ush"
#else
float4x4 ScreenToShadowMatrix;
// .x:DepthBias, .y:SlopeDepthBias, .z:ReceiverBias, .w: MaxSubjectZ - MinSubjectZ
float4 ProjectionDepthBiasParameters;
#endif
float4 LightPositionOrDirection;
float ShadowReceiverBias;
#if USE_FADE_PLANE || SUBPIXEL_SHADOW
float FadePlaneOffset;
float InvFadePlaneLength;
uint bCascadeUseFadePlane;
#endif
#if USE_PCSS
// PCSS specific parameters.
// - x: tan(0.5 * Directional Light Angle) in shadow projection space;
// - y: Max filter size in shadow tile UV space.
float4 PCSSParameters;
#endif
float4 ModulatedShadowColor;
float4 ShadowTileOffsetAndSize;
float3 ModulatedShadowBlendOp(float3 Source)
{
half4 Dest = half4(0, 0, 0, 0);
return Source.rgb*Dest.rgb;
}
// PCF falloff is overly blurry, apply to get
// a more reasonable look artistically. Should not be applied to
// other passes that target pbr (e.g., raytracing and denoising).
float ApplyPCFOverBlurCorrection(float Shadow)
{
return Square(Shadow);
}
#if SUBPIXEL_SHADOW
#include "HairStrands/HairStrandsVisibilityCommon.ush"
float2 ShadowNearAndFarDepth;
#endif
#include "Substrate/Substrate.ush"
/**
* Entry point for uniform manual PCF that supports lights using normal shadows.
*/
// Do not force early_fragment_tests on mobile, as it does not work with depth fetch on MALI
#if (FEATURE_LEVEL > FEATURE_LEVEL_ES3_1)
EARLYDEPTHSTENCIL
#endif
void Main(
in float4 SVPos : SV_POSITION,
in FStereoPSInput StereoInput,
out float4 OutColor : SV_Target0
)
{
#if USE_FADE_PLANE
const bool bUseFadePlane = true;
#endif
StereoSetupPS(StereoInput);
const FPQMPContext PQMPContext = PQMPInit(SVPos.xy);
float2 ScreenUV = float2(SVPos.xy * ResolvedView.BufferSizeAndInvSize.zw);
#if MOBILE_MULTI_VIEW && SHADING_PATH_MOBILE
float SceneW = CalcSceneDepth(ScreenUV, GetEyeIndex(StereoInput));
#else
float SceneW = CalcSceneDepth(ScreenUV);
#endif
bool bIsSubsurfaceCompatible = true;
const uint3 PixelCoord = uint3(floor(SVPos.xy), 0);
#if SUBPIXEL_SHADOW
const bool bUseFadePlane = bCascadeUseFadePlane > 0;
SceneW = ConvertFromDeviceZ(HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x);
bIsSubsurfaceCompatible = false;
#endif
float4 ScreenPosition = float4(GetScreenPositionForProjectionType(((ScreenUV.xy - ResolvedView.ScreenPositionScaleBias.wz) / ResolvedView.ScreenPositionScaleBias.xy), SceneW), SceneW, 1);
float4 ShadowPosition = float4(0, 0, 0, 0);
#if MOBILE_MULTI_VIEW && SHADING_PATH_MOBILE
ShadowPosition = mul(ScreenPosition, ResolvedView.MobileMultiviewShadowTransform);
ShadowPosition = mul(ShadowPosition, ScreenToShadowMatrix);
#else
ShadowPosition = mul(ScreenPosition, ScreenToShadowMatrix);
#endif
float3 TranslateWorldPosition = mul(ScreenPosition, ResolvedView.ScreenToTranslatedWorld).xyz;
float ShadowZ = 1 - ShadowPosition.z;
ShadowPosition.xyz /= ShadowPosition.w;
#if MODULATED_SHADOWS
// UE-29083 : work around precision issues with ScreenToShadowMatrix on low end devices.
ShadowPosition.xy *= ShadowTileOffsetAndSize.zw;
ShadowPosition.xy += ShadowTileOffsetAndSize.xy;
#endif
#if USE_PCSS
float3 ScreenPositionDDX = DDX(ScreenPosition.xyz);
float3 ScreenPositionDDY = DDY(ScreenPosition.xyz);
float4 ShadowPositionDDX = mul(float4(ScreenPositionDDX, 0), ScreenToShadowMatrix);
float4 ShadowPositionDDY = mul(float4(ScreenPositionDDY, 0), ScreenToShadowMatrix);
#if SPOT_LIGHT_PCSS
// perspective correction for derivatives, could be good enough and way cheaper to just use ddx(ScreenPosition)
ShadowPositionDDX.xyz -= ShadowPosition.xyz * ShadowPositionDDX.w;
ShadowPositionDDY.xyz -= ShadowPosition.xyz * ShadowPositionDDY.w;
#endif
#endif
// Clamp pixel depth in light space for shadowing opaque, because areas of the shadow depth buffer that weren't rendered to will have been cleared to 1
// We want to force the shadow comparison to result in 'unshadowed' in that case, regardless of whether the pixel being shaded is in front or behind that plane
float LightSpacePixelDepthForOpaque = min(ShadowZ, 0.99999f);
// Must not clamp for SSS shadowing, the subsurface gradient must continue past the far plane
float LightSpacePixelDepthForSSS = ShadowZ;
// fade out per-object shadow before cut-off, goes from 1 at start of fade plane to 0 at the end of the fade length.
float PerObjectDistanceFadeFraction = 1.0f - saturate((LightSpacePixelDepthForSSS - PerObjectShadowFadeStart) * InvPerObjectShadowFadeLength);
float Shadow = 1;
float SSSTransmission = 1;
float BlendFactor = 1;
// For debugging
#define UNFILTERED_SHADOW_PROJECTION 0
#if UNFILTERED_SHADOW_PROJECTION
{
Shadow = LightSpacePixelDepthForOpaque < Texture2DSampleLevel(ShadowDepthTexture, ShadowDepthTextureSampler, ShadowPosition.xy, 0).r;
}
#elif APPLY_TRANSLUCENCY_SHADOWS
{
Shadow = CalculateTranslucencyShadowing(ShadowPosition.xy, ShadowZ);
}
#elif USE_PCSS
{
FPCSSSamplerSettings Settings;
#if SPOT_LIGHT_PCSS
{
float CotanOuterCone = DeferredLightUniforms.SpotAngles.x * rsqrt(1. - DeferredLightUniforms.SpotAngles.x * DeferredLightUniforms.SpotAngles.x);
float WorldLightDistance = dot(DeferredLightUniforms.Direction, GetDeferredLightTranslatedWorldPosition() - TranslateWorldPosition);
Settings.ProjectedSourceRadius = 0.5 * DeferredLightUniforms.SourceRadius * CotanOuterCone / WorldLightDistance;
Settings.TanLightSourceAngle = 0;
}
#else
{
Settings.ProjectedSourceRadius = 0;
Settings.TanLightSourceAngle = PCSSParameters.x;
}
#endif
Settings.ShadowDepthTexture = ShadowDepthTexture;
Settings.ShadowDepthTextureSampler = ShadowDepthTextureSampler;
Settings.ShadowBufferSize = ShadowBufferSize;
Settings.ShadowTileOffsetAndSize = ShadowTileOffsetAndSize;
Settings.SceneDepth = LightSpacePixelDepthForOpaque;
Settings.TransitionScale = SoftTransitionScale.z;
Settings.MaxKernelSize = PCSSParameters.y;
Settings.SvPosition = SVPos.xy;
Settings.PQMPContext = PQMPContext;
Settings.DebugViewportUV = ScreenUV;
Shadow = DirectionalPCSS(Settings, ShadowPosition.xy, ShadowPositionDDX.xyz, ShadowPositionDDY.xyz);
}
#else // !USE_PCSS
{
#if SHADING_PATH_DEFERRED && !FORWARD_SHADING && !SUBPIXEL_SHADOW
// Attenuate soft transition based on the angle with the light and the shading normal (acts as a receiver bias)
const bool bIsDirectional = LightPositionOrDirection.w == 0;
const float3 LightDirection = bIsDirectional ? -LightPositionOrDirection.xyz : normalize(LightPositionOrDirection.xyz - TranslateWorldPosition);
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(SVPos.xy, 0)));
const float3 WorldNormal = TopLayerData.WorldNormal;
#else
FGBufferData GBufferData = GetGBufferData(ScreenUV);
const float3 WorldNormal = GBufferData.WorldNormal;
#endif
const float NoL = saturate(dot(WorldNormal, LightDirection));
#endif
FPCFSamplerSettings Settings;
Settings.ShadowDepthTexture = ShadowDepthTexture;
Settings.ShadowDepthTextureSampler = ShadowDepthTextureSampler;
Settings.ShadowBufferSize = ShadowBufferSize;
#if SHADING_PATH_DEFERRED && !FORWARD_SHADING && !SUBPIXEL_SHADOW
Settings.TransitionScale = SoftTransitionScale.z * lerp(ProjectionDepthBiasParameters.z, 1.0, NoL);
#else
Settings.TransitionScale = SoftTransitionScale.z;
#endif
Settings.SceneDepth = LightSpacePixelDepthForOpaque;
Settings.bSubsurface = false;
Settings.bTreatMaxDepthUnshadowed = false;
Settings.DensityMulConstant = 0;
Settings.ProjectionDepthBiasParameters = 0;
#if SHADING_PATH_MOBILE
Shadow = MobileShadowPCF(ShadowPosition.xy, Settings);
#else
Shadow = ManualPCF(ShadowPosition.xy, Settings);
#endif
}
#endif // !USE_PCSS
#if USE_FADE_PLANE || SUBPIXEL_SHADOW
if (bUseFadePlane)
{
// Create a blend factor which is one before and at the fade plane, and lerps to zero at the far plane.
BlendFactor = 1.0f - saturate((SceneW - FadePlaneOffset) * InvFadePlaneLength);
}
#endif
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && !FORWARD_SHADING && !APPLY_TRANSLUCENCY_SHADOWS && !SUBPIXEL_SHADOW
bool bHasSubsurface = false;
bool bHasSubsurfaceTransmission = false;
bool bIsEyeOrHair = false;
#if SUBTRATE_GBUFFER_FORMAT==1
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelCoord.xy, uint2(ResolvedView.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
FSubstratePixelHeader SubstrateHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture);
if (SubstrateHeader.IsSubstrateMaterial())
{
bIsEyeOrHair = SubstrateHeader.SubstrateGetBSDFType() == SUBSTRATE_BSDF_TYPE_HAIR || SubstrateHeader.SubstrateGetBSDFType() == SUBSTRATE_BSDF_TYPE_EYE;
bHasSubsurface = SubstrateHeader.HasSubsurface() || bIsEyeOrHair;
bHasSubsurfaceTransmission = SubstrateHeader.HasSubsurface();
}
#else // SUBTRATE_GBUFFER_FORMAT==1
FGBufferData GBufferData = GetGBufferData(ScreenUV);
{
bHasSubsurface = IsSubsurfaceModel(GBufferData.ShadingModelID);
bHasSubsurfaceTransmission = GBufferData.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE;
bIsEyeOrHair = GBufferData.ShadingModelID == SHADINGMODELID_HAIR || GBufferData.ShadingModelID == SHADINGMODELID_EYE;
}
#endif // SUBTRATE_GBUFFER_FORMAT==1
BRANCH
if (bIsSubsurfaceCompatible && bHasSubsurface)
{
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateSubsurfaceHeader SSSHeader = SubstrateLoadSubsurfaceHeader(Substrate.MaterialTextureArray, Substrate.FirstSliceStoringSubstrateSSSData, SVPos.xy);
const uint SSSType = SubstrateSubSurfaceHeaderGetSSSType(SSSHeader);
#endif
float Density = 1;
if (!bIsEyeOrHair)
{
#if SUBTRATE_GBUFFER_FORMAT==1
if (SSSType != SSS_TYPE_NONE)
{
// Disble accurate SSS transmission if SSS type is no using Diffusion or Diffusion Profile
bHasSubsurfaceTransmission = bHasSubsurfaceTransmission && (SSSType >= SSS_TYPE_DIFFUSION);
Density = SubsurfaceDensityFromOpacity(SubstrateSubSurfaceHeaderGetOpacity(SSSHeader));
}
#else // SUBTRATE_GBUFFER_FORMAT==1
Density = SubsurfaceDensityFromOpacity(GBufferData.CustomData.a);
#endif // SUBTRATE_GBUFFER_FORMAT==1
}
FPCFSamplerSettings Settings;
Settings.ShadowDepthTexture = ShadowDepthTexture;
Settings.ShadowDepthTextureSampler = ShadowDepthTextureSampler;
Settings.ShadowBufferSize = ShadowBufferSize;
Settings.TransitionScale = SoftTransitionScale.z;
Settings.SceneDepth = LightSpacePixelDepthForSSS + ProjectionDepthBiasParameters.x;
Settings.bSubsurface = true;
Settings.bTreatMaxDepthUnshadowed = false;
Settings.DensityMulConstant = Density * ProjectionDepthBiasParameters.w;
Settings.ProjectionDepthBiasParameters = ProjectionDepthBiasParameters.xw;
#if USE_TRANSMISSION
if (bHasSubsurfaceTransmission)
{
// This branch is only taken by SSS profiles and SSS per pixel diffusion methods.
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(SVPos.xy, 0)));
const float3 WorldNormal = TopLayerData.WorldNormal;
FTransmissionProfileParams TransmissionParams = InitTransmissionProfileParams();
if (SSSType == SSS_TYPE_DIFFUSION_PROFILE)
{
TransmissionParams = GetTransmissionProfileParams(SubstrateSubSurfaceHeaderGetProfileId(SSSHeader));
}
else if (SSSType == SSS_TYPE_DIFFUSION)
{
//For per pixel diffusion, use default transmission parameters (only NormalScale & ExtinctionScale are used)
const float3 ColoredMFP = SubstrateSubSurfaceHeaderGetMFP(SSSHeader);
const float GreyMFP = SubstrateShadowColoredMFPToGreyScaleMFP(ColoredMFP);
const float Extinction = SubstrateShadowMFPToExtinction(GreyMFP);
TransmissionParams.ExtinctionScale = Extinction;
}
#else // SUBTRATE_GBUFFER_FORMAT==1
float3 WorldNormal = GBufferData.WorldNormal;
FTransmissionProfileParams TransmissionParams = GetTransmissionProfileParams(ExtractSubsurfaceProfileInt(GBufferData));
#endif // SUBTRATE_GBUFFER_FORMAT==1
SSSTransmission = CalcTransmissionThickness(ScreenPosition.xyz, LightPositionAndInvRadius.xyz, WorldNormal, TransmissionParams, Settings);
}
else
#endif
{
// This branch is taken by Subsurface shading model (Wrap for Substrate)
// ideally we use a larger filter kernel for SSSbut as Gather4 makes that harder
SSSTransmission = ManualPCF(ShadowPosition.xy, Settings);
}
SSSTransmission = ApplyPCFOverBlurCorrection(SSSTransmission);
}
#endif
#if !USE_PCSS
// There is no point changing the shadow sharpen on PCSS, can directly reduce the light
// source angle that would actually make the algorithm to run faster.
Shadow = saturate( (Shadow - 0.5) * ShadowSharpen + 0.5 );
#endif
// 0 is shadowed, 1 is unshadowed
// RETURN_COLOR not needed unless writing to SceneColor;
// Apply PCF overblur correction for shadow when PCSS & PCF.
#define CORRECT_SHADOW_PCF_BLUR !UNFILTERED_SHADOW_PROJECTION && !APPLY_TRANSLUCENCY_SHADOWS
#if CORRECT_SHADOW_PCF_BLUR
Shadow = ApplyPCFOverBlurCorrection(Shadow);
#endif
float FadedShadow = lerp(1.0f, Shadow, ShadowFadeFraction * PerObjectDistanceFadeFraction);
#if FORWARD_SHADING || SHADING_PATH_MOBILE
float LightInfluenceMask = GetLightInfluenceMask(TranslateWorldPosition);
// Constrain shadowing from this light to pixels inside the light's influence, since other non-overlapping lights are packed into the same channel
FadedShadow = lerp(1, FadedShadow, LightInfluenceMask);
// Write into all channels, the write mask will constrain to the correct one
OutColor = EncodeLightAttenuation(FadedShadow);
#else
float FadedSSSShadow = lerp(1.0f, SSSTransmission, ShadowFadeFraction * PerObjectDistanceFadeFraction);
// the channel assignment is documented in ShadowRendering.cpp (look for Light Attenuation channel assignment)
OutColor = EncodeLightAttenuation(half4(FadedShadow, FadedSSSShadow, FadedShadow, FadedSSSShadow));
#endif
#if USE_FADE_PLANE || SUBPIXEL_SHADOW
// When the fade plane is in use for CSMs, we output the fade value in the alpha channel for blending.
if (bUseFadePlane)
{
OutColor.a = BlendFactor;
}
#endif
#if MODULATED_SHADOWS
OutColor.rgb = lerp(ModulatedShadowColor.rgb, float3(1, 1, 1), FadedShadow);
OutColor.a = 0;
#endif
}
// .x:DepthBias, y: SlopeDepthBias, z: MaxSlopeDepthBias,
float3 PointLightDepthBias;
// xy: depth projection parameters
float2 PointLightProjParameters;
/** Pixel shader for projecting a one pass point light shadow from a cube map. */
void MainOnePassPointLightPS(
in float4 SVPos : SV_POSITION,
in FStereoPSInput StereoInput,
out float4 OutColor : SV_Target0
)
{
StereoSetupPS(StereoInput);
float2 ScreenUV = float2( SVPos.xy * ResolvedView.BufferSizeAndInvSize.zw );
#if SHADING_PATH_MOBILE
float SceneW = CalcSceneDepth(ScreenUV, GetEyeIndex(StereoInput));
#else
float SceneW = CalcSceneDepth( ScreenUV );
#endif
#if SUBPIXEL_SHADOW
const uint3 PixelCoord = uint3(floor(SVPos.xy), 0);
const float HairSampleDepth = HairStrands.HairOnlyDepthTexture.Load(PixelCoord).x;
if (HairSampleDepth > 0)
{
SceneW = ConvertFromDeviceZ(HairSampleDepth);
}
else
{
discard;
}
#endif
float2 ScreenPosition = ( ScreenUV.xy - ResolvedView.ScreenPositionScaleBias.wz ) / ResolvedView.ScreenPositionScaleBias.xy;
float3 TranslateWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, SceneW), SceneW, 1), ResolvedView.ScreenToTranslatedWorld).xyz;
// For debugging
#define OUTPUT_CUBE_SHADOW_DEPTH_NO_FILTERING 0
float3 WorldSampleToLightVec = LightPositionAndInvRadius.xyz - TranslateWorldPosition.xyz;
#if OUTPUT_CUBE_SHADOW_DEPTH_NO_FILTERING
// Note: point light shadow depth is Z / W, not linear
#if USE_SEPARATE_SHADOW_DEPTH_CUBE_TEXTURE
float Shadow = TextureCubeSampleLevel(ShadowDepthCubeTexture2, ShadowDepthTextureSampler, WorldSampleToLightVec, 0);
#else
float Shadow = TextureCubeSampleLevel(ShadowDepthCubeTexture, ShadowDepthTextureSampler, WorldSampleToLightVec, 0);
#endif
#else
// Normal receiver bias: Increase depth based on the angle with the light and the shading normal.
// Unlike spot/rect/direction light, point lights performs depth bias only at projection time.
// This is why the slope bias drives the normal receiver bias here.
float SlopeBias = 0;
#if !SUBPIXEL_SHADOW
{
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(SVPos.xy, 0)));
const float3 WorldNormal = TopLayerData.WorldNormal;
#else // SUBTRATE_GBUFFER_FORMAT==1
#if SHADING_PATH_MOBILE
FGBufferData GBufferData = MobileFetchAndDecodeGBuffer(ScreenUV, SVPos);
#else
FGBufferData GBufferData = GetGBufferData(ScreenUV);
#endif
const float3 WorldNormal = GBufferData.WorldNormal;
#endif // SUBTRATE_GBUFFER_FORMAT==1
const float3 LightDirection = normalize(WorldSampleToLightVec);
const float NoL = saturate(dot(WorldNormal, LightDirection));
SlopeBias = (1 - NoL) * PointLightDepthBias.y;
}
#endif
float Shadow = CubemapHardwarePCF(TranslateWorldPosition, LightPositionAndInvRadius.xyz, LightPositionAndInvRadius.w, PointLightDepthBias.x, SlopeBias, PointLightDepthBias.z);
Shadow = saturate( (Shadow - 0.5) * ShadowSharpen + 0.5 );
Shadow = ApplyPCFOverBlurCorrection(Shadow);
#endif
float FadedShadow = lerp(1.0f, Shadow, ShadowFadeFraction);
#if FORWARD_SHADING
float LightInfluenceMask = GetLightInfluenceMask(TranslateWorldPosition);
FadedShadow = lerp(1, FadedShadow, LightInfluenceMask);
OutColor = EncodeLightAttenuation(FadedShadow);
#else
// Light attenuation buffer has been remapped.
// Point light shadows now write to the blue channel.
OutColor.b = EncodeLightAttenuation(FadedShadow);
OutColor.rga = 1;
// SSS is not correctly handled but at least it should be shadowed
OutColor.a = OutColor.b;
#endif
#if USE_TRANSMISSION
float Thickness = 1.0f;
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(SVPos.xy, 0)));
const FSubstrateSubsurfaceHeader SSSHeader = SubstrateLoadSubsurfaceHeader(Substrate.MaterialTextureArray, Substrate.FirstSliceStoringSubstrateSSSData, SVPos.xy);
const uint SSSType = SubstrateSubSurfaceHeaderGetSSSType(SSSHeader);
const bool IsSubsurfaceProfile = SubstrateSubSurfaceHeaderGetUseDiffusion(SSSHeader);
const float3 WorldNormal = TopLayerData.WorldNormal;
FTransmissionProfileParams TransmissionParams = InitTransmissionProfileParams();
if (SSSType == SSS_TYPE_DIFFUSION_PROFILE)
{
TransmissionParams = GetTransmissionProfileParams(SubstrateSubSurfaceHeaderGetProfileId(SSSHeader));
}
else if (SSSType == SSS_TYPE_DIFFUSION)
{
//For per pixel diffusion, use default transmission parameters (only NormalScale & ExtinctionScale are used)
const float3 ColoredMFP = SubstrateSubSurfaceHeaderGetMFP(SSSHeader);
const float GreyMFP = SubstrateShadowColoredMFPToGreyScaleMFP(ColoredMFP);
const float Extinction = SubstrateShadowMFPToExtinction(GreyMFP);
TransmissionParams.ExtinctionScale = Extinction;
}
#else // SUBTRATE_GBUFFER_FORMAT==1
// TODO: Use existing GBuffer data, instead of overwriting?
FGBufferData GBufferData = GetGBufferData(ScreenUV);
bool IsSubsurfaceProfile = GBufferData.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE;
float3 WorldNormal = GBufferData.WorldNormal;
FTransmissionProfileParams TransmissionParams = GetTransmissionProfileParams(ExtractSubsurfaceProfileInt(GBufferData));
#endif // SUBTRATE_GBUFFER_FORMAT==1
//if bSubsurface get SSS shadow,else opaque shadow.
if (IsSubsurfaceProfile)
{
Thickness = CalcTransmissionThickness(WorldNormal, TransmissionParams, WorldSampleToLightVec, PointLightProjParameters, LightPositionAndInvRadius.w, 0);
}
OutColor.a = IsSubsurfaceProfile ? EncodeLightAttenuation(Thickness) : OutColor.b;
#endif // USE_TRANSMISSION
}