// 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 }