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

470 lines
14 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#ifndef ALLOW_SSS_MATERIAL_OVERRIDE
#define ALLOW_SSS_MATERIAL_OVERRIDE 1
#endif
#include "SceneTexturesCommon.ush"
#include "SceneTextureParameters.ush"
#include "DeferredShadingCommon.ush"
float SquareInline(float X)
{
return X * X;
}
float3 EncodeNormalHelper(float3 SrcNormal, float QuantizationBias)
{
return SrcNormal * .5f + .5f;
}
float3 DecodeNormalHelper(float3 SrcNormal)
{
return SrcNormal * 2.0f - 1.0f;
}
uint EncodeQuantize6(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 63.0f + .5f + QuantizationBias),63u);
}
float DecodeQuantize6(uint Value)
{
return float(Value) / 63.0f;
}
uint EncodeQuantize6Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 63.0f + .5f + QuantizationBias),63u);
}
float DecodeQuantize6Sqrt(uint Value)
{
return SquareInline(float(Value) / 63.0f);
}
uint EncodeQuantize5(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 31.0f + .5f + QuantizationBias),31u);
}
float DecodeQuantize5(uint Value)
{
return float(Value) / 31.0f;
}
uint EncodeQuantize5Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 31.0f + .5f + QuantizationBias),31u);
}
float DecodeQuantize5Sqrt(uint Value)
{
return SquareInline(float(Value) / 31.0f);
}
uint EncodeQuantize4(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 15.0f + .5f + QuantizationBias),15u);
}
float DecodeQuantize4(uint Value)
{
return float(Value) / 15.0f;
}
uint EncodeQuantize4Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 15.0f + .5f + QuantizationBias),15u);
}
float DecodeQuantize4Sqrt(uint Value)
{
return SquareInline(float(Value) / 15.0f);
}
uint EncodeQuantize3(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 7.0f + .5f + QuantizationBias),7u);
}
float DecodeQuantize3(uint Value)
{
return float(Value) / 7.0f;
}
uint EncodeQuantize3Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 7.0f + .5f + QuantizationBias),7u);
}
float DecodeQuantize3Sqrt(uint Value)
{
return SquareInline(float(Value) / 7.0f);
}
uint EncodeQuantize2(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 3.0f + .5f + QuantizationBias),3u);
}
float DecodeQuantize2(uint Value)
{
return float(Value) / 3.0f;
}
uint EncodeQuantize2Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 3.0f + .5f + QuantizationBias),3u);
}
float DecodeQuantize2Sqrt(uint Value)
{
return SquareInline(float(Value) / 3.0f);
}
uint EncodeQuantize1(float Value, float QuantizationBias)
{
return min(uint(saturate(Value) * 1.0f + .5f + QuantizationBias),1u);
}
float DecodeQuantize1(uint Value)
{
return float(Value) / 1.0f;
}
uint EncodeQuantize1Sqrt(float Value, float QuantizationBias)
{
return min(uint(sqrt(saturate(Value)) * 1.0f + .5f + QuantizationBias),1u);
}
float DecodeQuantize1Sqrt(uint Value)
{
return SquareInline(float(Value) / 1.0f);
}
uint3 EncodeQuantize565(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize5(Value.x,QuantizationBias);
Ret.y = EncodeQuantize6(Value.y,QuantizationBias);
Ret.z = EncodeQuantize5(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize565(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize5(Value.x);
Ret.y = DecodeQuantize6(Value.y);
Ret.z = DecodeQuantize5(Value.z);
return Ret;
}
uint3 EncodeQuantize565Sqrt(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize5Sqrt(Value.x,QuantizationBias);
Ret.y = EncodeQuantize6Sqrt(Value.y,QuantizationBias);
Ret.z = EncodeQuantize5Sqrt(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize565Sqrt(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize5Sqrt(Value.x);
Ret.y = DecodeQuantize6Sqrt(Value.y);
Ret.z = DecodeQuantize5Sqrt(Value.z);
return Ret;
}
uint3 EncodeQuantize444(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize4(Value.x,QuantizationBias);
Ret.y = EncodeQuantize4(Value.y,QuantizationBias);
Ret.z = EncodeQuantize4(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize444(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize4(Value.x);
Ret.y = DecodeQuantize4(Value.y);
Ret.z = DecodeQuantize4(Value.z);
return Ret;
}
uint3 EncodeQuantize444Sqrt(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize4Sqrt(Value.x,QuantizationBias);
Ret.y = EncodeQuantize4Sqrt(Value.y,QuantizationBias);
Ret.z = EncodeQuantize4Sqrt(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize444Sqrt(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize4Sqrt(Value.x);
Ret.y = DecodeQuantize4Sqrt(Value.y);
Ret.z = DecodeQuantize4Sqrt(Value.z);
return Ret;
}
uint3 EncodeQuantize332(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize3(Value.x,QuantizationBias);
Ret.y = EncodeQuantize3(Value.y,QuantizationBias);
Ret.z = EncodeQuantize2(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize332(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize3(Value.x);
Ret.y = DecodeQuantize3(Value.y);
Ret.z = DecodeQuantize2(Value.z);
return Ret;
}
uint3 EncodeQuantize332Sqrt(float3 Value, float QuantizationBias)
{
uint3 Ret;
Ret.x = EncodeQuantize3Sqrt(Value.x,QuantizationBias);
Ret.y = EncodeQuantize3Sqrt(Value.y,QuantizationBias);
Ret.z = EncodeQuantize2Sqrt(Value.z,QuantizationBias);
return Ret;
}
float3 DecodeQuantize332Sqrt(uint3 Value)
{
float3 Ret;
Ret.x = DecodeQuantize3Sqrt(Value.x);
Ret.y = DecodeQuantize3Sqrt(Value.y);
Ret.z = DecodeQuantize2Sqrt(Value.z);
return Ret;
}
void EnvBRDFApproxFullyRoughHelper(inout float3 DiffuseColor, inout float3 SpecularColor)
{
// Factors derived from EnvBRDFApprox( SpecularColor, 1, 1 ) == SpecularColor * 0.4524 - 0.0024
DiffuseColor += SpecularColor * 0.45;
SpecularColor = 0;
// We do not modify Roughness here as this is done differently at different places.
}
void EnvBRDFApproxFullyRoughHelper(inout float3 DiffuseColor, inout float SpecularColor)
{
DiffuseColor += SpecularColor * 0.45;
SpecularColor = 0;
}
#define USES_GBUFFER_HELPER (FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && (MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED) && !FORWARD_SHADING)
#define FORCE_FULLY_ROUGH_HELPER (MATERIAL_FULLY_ROUGH)
// TODO: Move the BasePassPixelShader extra changes into here.
void GBufferPreEncode(inout FGBufferData GBuffer, bool bChecker, float GeometricAARoughness, inout half3 OriginalBaseColor, inout half OriginalSpecular, inout half OriginalMetallic, float QuantizationBias)
{
#if MATERIAL_NORMAL_CURVATURE_TO_ROUGHNESS
GBuffer.Roughness = max(GBuffer.Roughness, GeometricAARoughness);
#if MATERIAL_SHADINGMODEL_CLEAR_COAT
if (GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
GBuffer.CustomData.y = max(GBuffer.CustomData.y, GeometricAARoughness);
}
#endif
#endif
#if USES_GBUFFER_HELPER && (MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_EYE)
// SubsurfaceProfile applies the BaseColor in a later pass. Any lighting output in the base pass needs
// to separate specular and diffuse lighting in a checkerboard pattern
if (UseSubsurfaceProfile(GBuffer.ShadingModelID))
{
const bool bCheckerboardRequired = View.bSubsurfacePostprocessEnabled > 0 && View.bCheckerboardSubsurfaceProfileRendering > 0;
OriginalBaseColor = View.bSubsurfacePostprocessEnabled ? float3(1, 1, 1) : OriginalBaseColor;
if (bCheckerboardRequired)
{
// because we adjust the BaseColor here, we need StoredBaseColor
// we apply the base color later in SubsurfaceRecombinePS()
OriginalBaseColor = bChecker;
// in SubsurfaceRecombinePS() does not multiply with Specular so we do it here
GBuffer.SpecularColor *= !bChecker;
OriginalSpecular *= !bChecker;
}
}
#endif
GBuffer.DiffuseColor = OriginalBaseColor - OriginalBaseColor * OriginalMetallic;
#if USE_DEVELOPMENT_SHADERS
{
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
GBuffer.DiffuseColor = GBuffer.DiffuseColor * View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz;
GBuffer.SpecularColor = GBuffer.SpecularColor * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz;
}
#endif
#if !FORCE_FULLY_ROUGH_HELPER
if (View.RenderingReflectionCaptureMask) // Force material rendered in reflection capture to have an expanded albedo to try to be energy conservative (when specular is removed).
#endif
{
EnvBRDFApproxFullyRoughHelper(GBuffer.DiffuseColor, GBuffer.SpecularColor);
// When rendering reflection captures, GBuffer.Roughness is already forced to 1 using RoughnessOverrideParameter in GetMaterialRoughness.
}
// should move this
#if GBUFFER_HAS_DIFFUSE_SAMPLE_OCCLUSION
GBuffer.GenericAO = float(GBuffer.DiffuseIndirectSampleOcclusion) * (1.0f / 255.0f);
#elif ALLOW_STATIC_LIGHTING
// No space for AO. Multiply IndirectIrradiance by AO instead of storing.
GBuffer.GenericAO = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0);
#else
GBuffer.GenericAO = GBuffer.GBufferAO;
#endif
}
// Temporary extra copy of this function. The autogenerated GBuffer functions will require changing the include heirarchy, so copying this function to miminize
// the number of files that have to be touched on the initial checkin. Will remove duplicates once the first checkin is tested and stable.
void AdjustBaseColorAndSpecularColorForSubsurfaceProfileLightingCopyHack(inout float3 BaseColor, inout float3 SpecularColor, inout float Specular, bool bChecker)
{
// If this function is called in translucent pass, we should not modify the BaseColor, instead directly read the base color as it is.
// The translucent pass is called after the main post process subsurface scattering pass, it will not affect previous logic.
#if MATERIALBLENDING_TRANSLUCENT || !ALLOW_SSS_MATERIAL_OVERRIDE
return;
#else
#if SUBSURFACE_CHANNEL_MODE == 0
// If SUBSURFACE_CHANNEL_MODE is 0, we can't support full-resolution lighting, so we
// ignore View.bCheckerboardSubsurfaceProfileRendering
const bool bCheckerboardRequired = View.bSubsurfacePostprocessEnabled > 0;
#else
const bool bCheckerboardRequired = View.bSubsurfacePostprocessEnabled > 0 && View.bCheckerboardSubsurfaceProfileRendering > 0;
BaseColor = View.bSubsurfacePostprocessEnabled ? float3(1, 1, 1) : BaseColor;
#endif
if (bCheckerboardRequired)
{
// because we adjust the BaseColor here, we need StoredBaseColor
// we apply the base color later in SubsurfaceRecombinePS()
BaseColor = bChecker;
// in SubsurfaceRecombinePS() does not multiply with Specular so we do it here
SpecularColor *= !bChecker;
Specular *= !bChecker;
}
#endif
}
#ifndef INDIRECT_SAMPLE_COUNT
#define INDIRECT_SAMPLE_COUNT 8
#endif
void GBufferPostDecode(inout FGBufferData Ret, bool bChecker, bool bGetNormalizedNormal)
{
Ret.CustomData = HasCustomGBufferData(Ret.ShadingModelID) ? Ret.CustomData : half(0.0f);
// FirstPerson uses a bit in SelectiveOutputMask that is aliased with ZERO_PRECSHADOW_MASK when !ALLOW_STATIC_LIGHTING, so we explicitly skip this logic here.
#if ALLOW_STATIC_LIGHTING
Ret.PrecomputedShadowFactors = !(Ret.SelectiveOutputMask & 0x2) ? Ret.PrecomputedShadowFactors : ((Ret.SelectiveOutputMask & 0x4) ? half(0.0f) : half(1.0f));
#else
Ret.PrecomputedShadowFactors = half(1.0f);
#endif
Ret.Velocity = !(Ret.SelectiveOutputMask & 0x8) ? Ret.Velocity : half(0.0f);
bool bHasAnisotropy = (Ret.SelectiveOutputMask & 0x1);
Ret.StoredBaseColor = Ret.BaseColor;
Ret.StoredMetallic = Ret.Metallic;
Ret.StoredSpecular = Ret.Specular;
#if GBUFFER_HAS_DIFFUSE_SAMPLE_OCCLUSION
Ret.DiffuseIndirectSampleOcclusion = 255 * Ret.GenericAO;
Ret.GBufferAO = saturate(1.0 - float(countbits(Ret.DiffuseIndirectSampleOcclusion)) * rcp(float(INDIRECT_SAMPLE_COUNT)));
Ret.IndirectIrradiance = 1;
#elif ALLOW_STATIC_LIGHTING
Ret.GBufferAO = 1;
Ret.DiffuseIndirectSampleOcclusion = 0x0;
Ret.IndirectIrradiance = half(DecodeIndirectIrradiance(Ret.GenericAO.x));
#else
Ret.GBufferAO = Ret.GenericAO;
Ret.DiffuseIndirectSampleOcclusion = 0x0;
Ret.IndirectIrradiance = 1;
#endif
if(bGetNormalizedNormal)
{
Ret.WorldNormal = normalize(Ret.WorldNormal);
}
FLATTEN
if( Ret.ShadingModelID == SHADINGMODELID_EYE )
{
Ret.Metallic = 0.0;
#if IRIS_NORMAL
Ret.Specular = 0.25;
#endif
}
// derived from BaseColor, Metalness, Specular
{
Ret.SpecularColor = ComputeF0(Ret.Specular, Ret.BaseColor, Ret.Metallic);
if (UseSubsurfaceProfile(Ret.ShadingModelID))
{
AdjustBaseColorAndSpecularColorForSubsurfaceProfileLightingCopyHack(Ret.BaseColor, Ret.SpecularColor, Ret.Specular, bChecker);
}
Ret.DiffuseColor = Ret.BaseColor - Ret.BaseColor * Ret.Metallic;
#if USE_DEVELOPMENT_SHADERS
{
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
Ret.DiffuseColor = Ret.DiffuseColor * View.DiffuseOverrideParameter.www + View.DiffuseOverrideParameter.xyz;
Ret.SpecularColor = Ret.SpecularColor * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz;
}
#endif //USE_DEVELOPMENT_SHADERS
}
if (bHasAnisotropy)
{
Ret.WorldTangent = half3(DecodeNormal(Ret.WorldTangent));
Ret.Anisotropy = half(Ret.Anisotropy * 2.0f - 1.0f);
if(bGetNormalizedNormal)
{
Ret.WorldTangent = normalize(Ret.WorldTangent);
}
}
else
{
Ret.WorldTangent = 0;
Ret.Anisotropy = 0;
}
// This requires cleanup. Shader code that uses GBuffer.SelectiveOutputMask expects the outputmask to be in
// bits [4:7], but it gets packed as bits [0:3] in the flexible gbuffer since we might move it around.
Ret.SelectiveOutputMask = Ret.SelectiveOutputMask << 4;
}