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

220 lines
8.2 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Common.ush"
#ifndef SHADING_PATH_MOBILE
#define SHADING_PATH_MOBILE 0
#endif
#ifndef SHADING_PATH_DEFERRED
#define SHADING_PATH_DEFERRED 0
#endif
///////////////////////////////////////////////////////////////////////////////
// Shading models
// SHADINGMODELID_* occupy the 4 low bits of an 8bit channel and SKIP_* occupy the 4 high bits
#define SHADINGMODELID_UNLIT 0
#define SHADINGMODELID_DEFAULT_LIT 1
#define SHADINGMODELID_SUBSURFACE 2
#define SHADINGMODELID_PREINTEGRATED_SKIN 3
#define SHADINGMODELID_CLEAR_COAT 4
#define SHADINGMODELID_SUBSURFACE_PROFILE 5
#define SHADINGMODELID_TWOSIDED_FOLIAGE 6
#define SHADINGMODELID_HAIR 7
#define SHADINGMODELID_CLOTH 8
#define SHADINGMODELID_EYE 9
#define SHADINGMODELID_SINGLELAYERWATER 10
#define SHADINGMODELID_THIN_TRANSLUCENT 11
#define SHADINGMODELID_SUBSTRATE 12 // Temporary while we convert everything to Substrate
#define SHADINGMODELID_NUM 13
#define SHADINGMODELID_MASK 0xF // 4 bits reserved for ShadingModelID
// The flags are defined so that 0 value has no effect!
// These occupy the 4 high bits in the same channel as the SHADINGMODELID_*
#define HAS_ANISOTROPY_MASK (1 << 4)
#define SKIP_PRECSHADOW_MASK (1 << 5)
#define ZERO_PRECSHADOW_MASK (1 << 6)
#define IS_FIRST_PERSON_MASK (1 << 6) // Technically not related to the SelectiveOutputMask, but IS_FIRST_PERSON_MASK shares a bit with ZERO_PRECSHADOW_MASK and consequently only works if ALLOW_STATIC_LIGHTING is 0.
#define SKIP_VELOCITY_MASK (1 << 7)
#define SSS_PROFILE_ID_INVALID 256
#define SSS_PROFILE_ID_PERPIXEL 512
// for debugging and to visualize
float3 GetShadingModelColor(uint ShadingModelID)
{
// TODO: PS4 doesn't optimize out correctly the switch(), so it thinks it needs all the Samplers even if they get compiled out
// This will get fixed after launch per Sony...
#if PS4_PROFILE
if (ShadingModelID == SHADINGMODELID_UNLIT) return float3(0.1f, 0.1f, 0.2f); // Dark Blue
else if (ShadingModelID == SHADINGMODELID_DEFAULT_LIT) return float3(0.1f, 1.0f, 0.1f); // Green
else if (ShadingModelID == SHADINGMODELID_SUBSURFACE) return float3(1.0f, 0.1f, 0.1f); // Red
else if (ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN) return float3(0.6f, 0.4f, 0.1f); // Brown
else if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) return float3(0.1f, 0.4f, 0.4f);
else if (ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE) return float3(0.2f, 0.6f, 0.5f); // Cyan
else if (ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE) return float3(0.2f, 0.2f, 0.8f); // Blue
else if (ShadingModelID == SHADINGMODELID_HAIR) return float3(0.6f, 0.1f, 0.5f);
else if (ShadingModelID == SHADINGMODELID_CLOTH) return float3(0.7f, 1.0f, 1.0f);
else if (ShadingModelID == SHADINGMODELID_EYE) return float3(0.3f, 1.0f, 1.0f);
else if (ShadingModelID == SHADINGMODELID_SINGLELAYERWATER) return float3(0.5f, 0.5f, 1.0f);
else if (ShadingModelID == SHADINGMODELID_THIN_TRANSLUCENT) return float3(1.0f, 0.8f, 0.3f);
else if (ShadingModelID == SHADINGMODELID_SUBSTRATE) return float3(1.0f, 1.0f, 0.0f);
else return float3(1.0f, 1.0f, 1.0f); // White
#else
switch(ShadingModelID)
{
case SHADINGMODELID_UNLIT: return float3(0.1f, 0.1f, 0.2f); // Dark Blue
case SHADINGMODELID_DEFAULT_LIT: return float3(0.1f, 1.0f, 0.1f); // Green
case SHADINGMODELID_SUBSURFACE: return float3(1.0f, 0.1f, 0.1f); // Red
case SHADINGMODELID_PREINTEGRATED_SKIN: return float3(0.6f, 0.4f, 0.1f); // Brown
case SHADINGMODELID_CLEAR_COAT: return float3(0.1f, 0.4f, 0.4f); // Brown
case SHADINGMODELID_SUBSURFACE_PROFILE: return float3(0.2f, 0.6f, 0.5f); // Cyan
case SHADINGMODELID_TWOSIDED_FOLIAGE: return float3(0.2f, 0.2f, 0.8f); // Cyan
case SHADINGMODELID_HAIR: return float3(0.6f, 0.1f, 0.5f);
case SHADINGMODELID_CLOTH: return float3(0.7f, 1.0f, 1.0f);
case SHADINGMODELID_EYE: return float3(0.3f, 1.0f, 1.0f);
case SHADINGMODELID_SINGLELAYERWATER: return float3(0.5f, 0.5f, 1.0f);
case SHADINGMODELID_THIN_TRANSLUCENT: return float3(1.0f, 0.8f, 0.3f);
case SHADINGMODELID_SUBSTRATE: return float3(1.0f, 1.0f, 0.0f);
default: return float3(1.0f, 1.0f, 1.0f); // White
}
#endif
}
#define SHADINGMODEL_REQUIRES_BACKFACE_LIGHTING (MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || SUBSTRATE_ENABLED)
bool GetShadingModelRequiresBackfaceLighting(uint ShadingModelID)
{
return ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE;
}
///////////////////////////////////////////////////////////////////////////////
// Shading parameterisation
float F0ToDielectricSpecular(float F0)
{
return saturate(F0 / 0.08f);
}
float F0RGBToF0(float3 F0)
{
return max3(F0.r, F0.g, F0.b);
}
float F0RGBToDielectricSpecular(float3 F0)
{
return F0ToDielectricSpecular(F0RGBToF0(F0));
}
half DielectricSpecularToF0(half Specular)
{
return half(0.08f * Specular);
}
// [Burley, "Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering"]
float DielectricF0ToIor(float F0)
{
return 2.0f / (1.0f - sqrt(min(F0,0.99))) - 1.0f;
}
float DielectricF0RGBToIor(float3 F0)
{
return DielectricF0ToIor(F0RGBToF0(F0));
}
float DielectricIorToF0(float Ior)
{
const float F0Sqrt = (Ior-1)/(Ior+1);
const float F0 = F0Sqrt*F0Sqrt;
return F0;
}
// Anything with Specular less than 2% is physically impossible and is instead considered to be shadowing.
// See F_Schlick implementation.
float GetF0MicroOcclusionThreshold() { return 0.02f; }
float F0ToMicroOcclusion(float F0) { return saturate(50.0 * F0); }
float3 F0ToMicroOcclusion(float3 F0) { return saturate(50.0 * F0); }
float F0RGBToMicroOcclusion(float3 F0)
{
return F0ToMicroOcclusion(F0RGBToF0(F0));
}
half3 ComputeF0(half Specular, half3 BaseColor, half Metallic)
{
return lerp(DielectricSpecularToF0(Specular).xxx, BaseColor, Metallic.xxx);
}
float3 ComputeF90(float3 F0, float3 EdgeColor, float Metallic)
{
return lerp(1.0, EdgeColor, Metallic.xxx);
}
float3 ComputeDiffuseAlbedo(float3 BaseColor, float Metallic)
{
return BaseColor - BaseColor * Metallic;
}
float MakeRoughnessSafe(float Roughness, float MinRoughness=0.001f)
{
return clamp(Roughness, MinRoughness, 1.0f);
}
float F0ToMetallic(float F0)
{
// Approximate the metallic input from F0 with a small lerp region
const float FullMetalBeginF0 = 0.08f; // Instead of DiamondF0 = 0.24, the metallic region starts right after metallic >0 and specular=1 to match with legacy.
const float FullMetalEndF0 = 0.4f; // roughly the end of semi-conductor
// This is compatible with UE shading model mapping allowing F0 to take a value up to 0.08 for dielectric.
return saturate((F0 - FullMetalBeginF0) / (FullMetalEndF0 - FullMetalBeginF0));
}
float F0RGBToMetallic(float3 F0)
{
return F0ToMetallic(F0RGBToF0(F0));
}
///////////////////////////////////////////////////////////////////////////////
// Layering, coverage and transmittance
struct FVerticalLayeringInfo
{
float TransmittanceTopAndBottom; // Ratio of transmittance of both top and bottom surface only
float TransmittanceOnlyBottom; // Ratio of transmittance of bottom surface only
float TransmittanceOnlyTop; // Ratio of transmittance of top surface only
float SurfaceBottom; // Ratio of bottom surface visible
float SurfaceTop; // Ratio of top surface visible
float Coverage; // Ratio of any surface is visible
float NoSurface; // Ratio of no surface is visible
};
// This function returns surface information after a layering of two slab of matter having difference and uncorelated coverage.
// See https://www.desmos.com/calculator/qg5yc3nnr4
FVerticalLayeringInfo GetVerticalLayeringInfo(const float TopCoverage, const float BottomCoverage)
{
FVerticalLayeringInfo Info;
Info.TransmittanceTopAndBottom = TopCoverage * BottomCoverage;
Info.TransmittanceOnlyBottom = (1.0f - TopCoverage) * BottomCoverage;
Info.TransmittanceOnlyTop = (1.0f - BottomCoverage) * TopCoverage;
Info.SurfaceBottom = Info.TransmittanceOnlyBottom;
Info.SurfaceTop = TopCoverage; // == Info.TransmittanceOnlyTop + Info.TransmittanceTopAndBottom;
Info.Coverage = Info.SurfaceTop + Info.SurfaceBottom; // == Info.TransmittanceTopAndBottom + Info.TransmittanceOnlyBottom + Info.TransmittanceOnlyTop;// == TopCoverage + Info.TransmittanceOnlyBottom
Info.NoSurface = 1.0f - Info.Coverage;
return Info;
}