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

137 lines
5.6 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessSubsurfaceCommon.usf: Screenspace subsurface scattering common header.
=============================================================================*/
#pragma once
#include "Common.ush"
#include "SubsurfaceProfileCommon.ush"
#include "BurleyNormalizedSSSCommon.ush"
// the same to FSubsurfaceTiles::ETileType
#define SUBSURFACE_TILE_TYPE_AFIS 0
#define SUBSURFACE_TILE_TYPE_SEPARABLE 1
#define SUBSURFACE_TILE_TYPE_PASSTHROUGH 2
#define SUBSURFACE_TILE_TYPE_ALLNONPASSTHROUGH 3
#define SUBSURFACE_TILE_TYPE_ALL 4
#define SUBSURFACE_TILE_TYPE_COUNT 5
#define TYPE_SEPARABLE 0x1
#define TYPE_BURLEY 0x2
#define TYPE_IN_VIEWPORT 0x4
// x:Radius*DistanceToProjectionWindow/KernelSize*0.5, y:DistanceToProjectionWindow, z:OverrideNumSamples, w: MinGenerateMipsTileCount
float4 SubsurfaceParams;
groupshared uint SubsurfaceTypeFlag;
// fetch surface albedo and diffuse mean free path
// The diffuse mean free path is modulated by world scale and a custom term passed in
// which is the opacity texture.
#if SUBTRATE_GBUFFER_FORMAT==1
FBurleyParameter GetBurleyParameters(FSubstrateSubsurfaceData SSSData)
{
FBurleyParameter Out = (FBurleyParameter)0;
if (SubstrateSubSurfaceHeaderGetIsProfile(SSSData.Header))
{
uint ProfileId = SubstrateSubSurfaceHeaderGetProfileId(SSSData.Header);
float ProfileRadiusScale = SubstrateSubSurfaceHeaderGetProfileRadiusScale(SSSData.Header);
Out.SurfaceAlbedo = GetSubsurfaceProfileSurfaceAlbedo(ProfileId);
Out.DiffuseMeanFreePath = DecodeDiffuseMeanFreePath(GetSubsurfaceProfileDiffuseMeanFreePath(ProfileId));
Out.WorldUnitScale = DecodeWorldUnitScale(GetSubsurfaceProfileWorldUnitScale(ProfileId)) * BURLEY_CM_2_MM;
Out.SurfaceOpacity = ProfileRadiusScale;
Out.DiffuseMeanFreePath *= Out.SurfaceOpacity;
}
else if (SubstrateSubSurfaceHeaderGetUseDiffusion(SSSData.Header))
{
float3 SSSMFP = SubstrateSubSurfaceHeaderGetMFP(SSSData.Header);
float3 SSSBaseColor = SubstrateSubsurfaceExtrasGetBaseColor(SSSData.Extras);
// Decode the MFP stored as Float111110 in a uint over a RGBA8 unorm
const float3 MFP = SSSMFP * BURLEY_CM_2_MM; // Stored DMFP is in cm, converted into mm
const float MaxMFP = max3(MFP.x, MFP.y, MFP.z);
const uint IndexOfMaxMfp = (MFP.r == MaxMFP) ? 0 : ((MFP.g == MaxMFP) ? 1 : 2);
const float3 DMFP = GetDMFPFromMFPApprox(SSSBaseColor, MFP);
Out.SurfaceAlbedo = float4(SSSBaseColor, SSSBaseColor[IndexOfMaxMfp]);
Out.DiffuseMeanFreePath = float4(DMFP, DMFP[IndexOfMaxMfp]);
Out.WorldUnitScale = 1.0f; // no world unit scale change.
// SUBSTRATE_TODO smooth transistion based on pixel footprint w.r.t. MFPRadius and pixel footprint
Out.SurfaceOpacity = 1.0f;
Out.DiffuseMeanFreePath *= Out.SurfaceOpacity;
}
return Out;
}
#else
FBurleyParameter GetBurleyParameters(uint SubsurfaceProfileInt, FGBufferData GBuffer)
{
FBurleyParameter Out = (FBurleyParameter)0;
Out.SurfaceAlbedo = GetSubsurfaceProfileSurfaceAlbedo(SubsurfaceProfileInt);
Out.DiffuseMeanFreePath = DecodeDiffuseMeanFreePath(GetSubsurfaceProfileDiffuseMeanFreePath(SubsurfaceProfileInt));
Out.WorldUnitScale = DecodeWorldUnitScale(GetSubsurfaceProfileWorldUnitScale(SubsurfaceProfileInt)) * BURLEY_CM_2_MM;
// Mask should always be true, we can probably ignore it?
Out.SurfaceOpacity = UseSubsurfaceProfile(GBuffer.ShadingModelID) ? GBuffer.CustomData.a : 0.0f;
Out.DiffuseMeanFreePath *= Out.SurfaceOpacity;
return Out;
}
#endif
// The extent is for the subsurface texture.
float2 CalculateBurleyScale(float WorldUnitScale, float DepthAtCenter, float2 ViewportSize, float2 Extent, float2 ExtentInverse)
{
float2 BurleyScale = WorldUnitScale;
float SSSScaleX = SubsurfaceParams.x;
BurleyScale *= SSSScaleX / DepthAtCenter;
// cast from cm to mm for depth, and remove the effect of SUBSURFACE_KERNEL_SIZE.
BurleyScale *= SUBSURFACE_KERNEL_SIZE / BURLEY_CM_2_MM;
// account for Screen Percentage/Dyanmic Resolution Scaling
BurleyScale *= (ViewportSize.x * ExtentInverse.x);
BurleyScale.y *= (Extent.x * ExtentInverse.y);
return BurleyScale;
}
// Given the Depth and the BurleyParameter, figure out the actual radius of the center pixel in MM,
// taking into account the depth and screen dimensions.
float CalculateCenterSampleRadiusInMM(FBurleyParameter BurleyParameter, float2 BurleyScale, float2 ExtentInverse)
{
float DiffuseMeanFreePath = GetDiffuseMeanFreePathForSampling(BurleyParameter.DiffuseMeanFreePath);
float A = GetComponentForScalingFactorEstimation(BurleyParameter.SurfaceAlbedo);
float S = GetScalingFactor(A);
float3 S3D = GetScalingFactor3D(BurleyParameter.SurfaceAlbedo.xyz);
// In the reference function, UVOffset = BurleyScale * RadiusInMM
// float2 UVOffset = BurleyScale*BurleySampleInfo.RadiusInMM;
// So, given the UV offset, we can find the distance in mm as:
// float DistInMM = UvOffset.x/BurleyScale.x + UvOffset.y/BurleyScale.y;
// But for stability, we can just average them.
float CenterSampleRadiusInMM = 0.5f * (ExtentInverse.x/BurleyScale.x + ExtentInverse.y/BurleyScale.y);
return CenterSampleRadiusInMM;
}
float CalculateCenterSampleCdf(FBurleyParameter BurleyParameter, float CenterSampleRadiusInMM)
{
float DiffuseMeanFreePathForSampling = GetDiffuseMeanFreePathForSampling(BurleyParameter.DiffuseMeanFreePath);
float A = GetComponentForScalingFactorEstimation(BurleyParameter.SurfaceAlbedo);
float S = GetScalingFactor(A);
float D = DiffuseMeanFreePathForSampling / S;
float CenterSampleRadiusCdf = GetCDF(D.x,CenterSampleRadiusInMM,0);
return CenterSampleRadiusCdf;
}