Files
UnrealEngineUWP/Engine/Shaders/Private/SubsurfaceProfileCommon.ush
Tiantian Xie bcb085c183 Improve the specular matching between raster and path tracer for subsurface scattering.
1. Use the decoding used by raster for the path tracer.
2. Use direct encoding from material roughness to lobe roughness instead of going through the average, which leads to underestimation (2%) and clamping (20%) of the value space.

Also clean up the code a little.

#jira UE-148244
#rb chris.kulla, sebastien.hillaire, krzysztof.narkowicz
#preflight 624f17ce6d3bb118aaf08c74

[CL 19673284 by Tiantian Xie in ue5-main branch]
2022-04-07 13:41:02 -04:00

143 lines
6.8 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================================
SubsurfaceProfileCommon.ush: Subsurface scattering parameter lookup / decoding constants.
=============================================================================================*/
#pragma once
///////////////////////////////////////////////////////////////////////////////////////////////
// Describe SSS profile texture (data offsets & size)
// NOTE: Changing offsets below requires updating all instances of #SSSS_CONSTANTS
// TODO: This needs to be defined in a single place and shared between C++ and shaders!
#define SSSS_TINT_SCALE_OFFSET 0
#define BSSS_SURFACEALBEDO_OFFSET (SSSS_TINT_SCALE_OFFSET+1)
#define BSSS_DMFP_OFFSET (BSSS_SURFACEALBEDO_OFFSET+1)
#define SSSS_TRANSMISSION_OFFSET (BSSS_DMFP_OFFSET+1)
#define SSSS_BOUNDARY_COLOR_BLEED_OFFSET (SSSS_TRANSMISSION_OFFSET+1)
#define SSSS_DUAL_SPECULAR_OFFSET (SSSS_BOUNDARY_COLOR_BLEED_OFFSET+1)
#define SSSS_KERNEL0_OFFSET (SSSS_DUAL_SPECULAR_OFFSET+1)
#define SSSS_KERNEL0_SIZE 13
#define SSSS_KERNEL1_OFFSET (SSSS_KERNEL0_OFFSET + SSSS_KERNEL0_SIZE)
#define SSSS_KERNEL1_SIZE 9
#define SSSS_KERNEL2_OFFSET (SSSS_KERNEL1_OFFSET + SSSS_KERNEL1_SIZE)
#define SSSS_KERNEL2_SIZE 6
#define SSSS_KERNEL_TOTAL_SIZE (SSSS_KERNEL0_SIZE + SSSS_KERNEL1_SIZE + SSSS_KERNEL2_SIZE)
#define BSSS_TRANSMISSION_PROFILE_OFFSET (SSSS_KERNEL0_OFFSET + SSSS_KERNEL_TOTAL_SIZE)
#define BSSS_TRANSMISSION_PROFILE_SIZE 32
#define SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE 5.0f // See MaxTransmissionProfileDistance in ComputeTransmissionProfile(), SeparableSSS.cpp
#define SSSS_MAX_DUAL_SPECULAR_ROUGHNESS 2.0f
// Threshold value at which model switches from SSS to default lit
#define SSSS_OPACITY_THRESHOLD_EPS 0.10
///////////////////////////////////////////////////////////////////////////////////////////////
// Burley constants
#define BURLEY_MM_2_CM 0.1f
#define BURLEY_CM_2_MM 10.0f
// exactly one of these should be true
#define LIGHT_PERPENDICULAR 0
#define LIGHT_DIFFUSESURFACE 0
#define LIGHT_PERPENDICULAR_DIFFUSE_SURFACE 1
// Consistent in BurleyNormalizedSSSCommon.ush and SubsurfaceProfile.cpp
#define SSS_TYPE_BURLEY 0
#define SSS_TYPE_SSSS 1
// Make sure UIMax|ClampMax of WorldUNitScale * ENC_WORLDUNITSCALE_IN_CM_TO_UNIT <= 1
#define ENC_WORLDUNITSCALE_IN_CM_TO_UNIT 0.02f
#define DEC_UNIT_TO_WORLDUNITSCALE_IN_CM 1/ENC_WORLDUNITSCALE_IN_CM_TO_UNIT
// Make sure UIMax|ClampMax of DiffuseMeanFreePath * ENC_DIFFUSEMEANFREEPATH_IN_MM_TO_UNIT <= 1
#define ENC_DIFFUSEMEANFREEPATH_IN_MM_TO_UNIT (0.01f*0.2f)
#define DEC_UNIT_TO_DIFFUSEMEANFREEPATH_IN_MM 1/ENC_DIFFUSEMEANFREEPATH_IN_MM_TO_UNIT
///////////////////////////////////////////////////////////////////////////////////////////////
// Accessors
half4 GetSubsurfaceProfileTexture(uint SampleIndex, uint SubsurfaceProfileInt)
{
// One profile per line, encoded using constants above. See FSubsurfaceProfileTexture::CreateTexture() in SubsurfaceProfile.cpp.
#if COMPILER_GLSL_ES3_1 // Force to use a point sampler for Texture2D.Load on OpenGLES platform
return View.SSProfilesTexture.SampleLevel(View.SSProfilesSampler, (uint2(SampleIndex, SubsurfaceProfileInt) + float2(0.5f, 0.5f)) * View.SSProfilesTextureSizeAndInvSize.zw, 0);
#else
return View.SSProfilesTexture.Load(int3(SampleIndex, SubsurfaceProfileInt, 0));
#endif
}
half4 GetSubsurfaceProfileTexture(Texture2D InSSProfilesTexture, SamplerState InSSProfilesSampler, float4 InSSProfilesTextureSizeAndInvSize, uint SampleIndex, uint SubsurfaceProfileInt)
{
// One profile per line, encoded using constants above. See FSubsurfaceProfileTexture::CreateTexture() in SubsurfaceProfile.cpp.
#if COMPILER_GLSL_ES3_1 // Force to use a point sampler for Texture2D.Load on OpenGLES platform
return InSSProfilesTexture.SampleLevel(InSSProfilesSampler, (uint2(SampleIndex, SubsurfaceProfileInt) + float2(0.5f, 0.5f)) * InSSProfilesTextureSizeAndInvSize.zw, 0);
#else
return InSSProfilesTexture.Load(int3(SampleIndex, SubsurfaceProfileInt, 0));
#endif
}
void GetSubsurfaceProfileDualSpecular(uint SubsurfaceProfileInt, float Roughness, float Opacity, out float LobeRoughness0, out float LobeRoughness1, out float LobeMix)
{
const half4 Data = GetSubsurfaceProfileTexture(SSSS_DUAL_SPECULAR_OFFSET, SubsurfaceProfileInt);
// Smooth blend out dual specular when opacity is low, we have the extra SSSS_OPACITY_THRESHOLD_EPS so that we fade out by the time we
// get to 0.01, as opposed to 0.0.
float MaterialRoughnessToLobeRoughness0 = lerp(1.0f, Data.x * SSSS_MAX_DUAL_SPECULAR_ROUGHNESS, saturate((Opacity - SSSS_OPACITY_THRESHOLD_EPS) * 10.0f));
float MaterialRoughnessToLobeRoughness1 = lerp(1.0f, Data.y * SSSS_MAX_DUAL_SPECULAR_ROUGHNESS, saturate((Opacity - SSSS_OPACITY_THRESHOLD_EPS) * 10.0f));
LobeMix = Data.z;
// Avoid specular explosion, and approximate `area lights`.
LobeRoughness0 = max(saturate(Roughness * MaterialRoughnessToLobeRoughness0), 0.02f);
LobeRoughness1 = saturate(Roughness * MaterialRoughnessToLobeRoughness1);
}
// Surface albedo and mean free path length
float4 GetSubsurfaceProfileSurfaceAlbedo(uint SubsurfaceProfileInt)
{
return GetSubsurfaceProfileTexture(BSSS_SURFACEALBEDO_OFFSET, SubsurfaceProfileInt);
}
float4 GetSubsurfaceProfileDiffuseMeanFreePath(uint SubsurfaceProfileInt)
{
return GetSubsurfaceProfileTexture(BSSS_DMFP_OFFSET, SubsurfaceProfileInt);
}
float GetSubsurfaceProfileWorldUnitScale(uint SubsurfaceProfileInt)
{
return GetSubsurfaceProfileTexture(SSSS_TINT_SCALE_OFFSET, SubsurfaceProfileInt).a;
}
float DecodeWorldUnitScale(float EncodedWorldUnitScale)
{
return EncodedWorldUnitScale * DEC_UNIT_TO_WORLDUNITSCALE_IN_CM;
}
float4 DecodeDiffuseMeanFreePath(float4 EncodedDiffuseMeanFreePath)
{
return EncodedDiffuseMeanFreePath * DEC_UNIT_TO_DIFFUSEMEANFREEPATH_IN_MM;
}
bool GetSubsurfaceTransmittanceProfileUseBurley(uint SubsurfaceProfileInt)
{
half Type = GetSubsurfaceProfileTexture(SSSS_BOUNDARY_COLOR_BLEED_OFFSET, SubsurfaceProfileInt).a;
return abs(Type - SSS_TYPE_BURLEY) < 0.01f;
}
bool GetSubsurfaceProfileUseBurley(uint SubsurfaceProfileInt)
{
half Type = GetSubsurfaceProfileTexture(SSSS_BOUNDARY_COLOR_BLEED_OFFSET, SubsurfaceProfileInt).a;
return abs(Type - SSS_TYPE_BURLEY) < 0.01f;
}
bool GetSubsurfaceProfileUseSeparable(uint SubsurfaceProfileInt)
{
half Type = GetSubsurfaceProfileTexture(SSSS_BOUNDARY_COLOR_BLEED_OFFSET, SubsurfaceProfileInt).a;
return abs(Type - SSS_TYPE_SSSS) < 0.01f;
}
float4 GetSubsurfaceProfileDMFPInCm(int SubsurfaceProfileInt)
{
const float4 DiffuseMeanFreePath = DecodeDiffuseMeanFreePath(GetSubsurfaceProfileDiffuseMeanFreePath(SubsurfaceProfileInt));
const float WorldUnitScale = DecodeWorldUnitScale(GetSubsurfaceProfileWorldUnitScale(SubsurfaceProfileInt));
return DiffuseMeanFreePath * WorldUnitScale; // In cm
}