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

341 lines
9.1 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
SHCommon.usf: Spherical Harmonic functionality
=============================================================================*/
#pragma once
/** The SH coefficients for the projection of a function that maps directions to scalar values. */
struct FOneBandSHVector
{
half V;
};
/** The SH coefficients for the projection of a function that maps directions to RGB values. */
struct FOneBandSHVectorRGB
{
FOneBandSHVector R;
FOneBandSHVector G;
FOneBandSHVector B;
};
/** The SH coefficients for the projection of a function that maps directions to scalar values. */
struct FTwoBandSHVector
{
half4 V;
};
/** The SH coefficients for the projection of a function that maps directions to RGB values. */
struct FTwoBandSHVectorRGB
{
FTwoBandSHVector R;
FTwoBandSHVector G;
FTwoBandSHVector B;
};
/** The SH coefficients for the projection of a function that maps directions to scalar values. */
struct FThreeBandSHVector
{
half4 V0;
half4 V1;
half V2;
#if defined(NEED_SH_VECTOR_PADDING) && NEED_SH_VECTOR_PADDING==1
// Add padding to workaround a bug with some shader compilers when storing structs in LDS
half3 Pad;
#endif
};
struct FThreeBandSHVectorRGB
{
FThreeBandSHVector R;
FThreeBandSHVector G;
FThreeBandSHVector B;
};
FTwoBandSHVectorRGB MulSH(FTwoBandSHVectorRGB A, half Scalar)
{
FTwoBandSHVectorRGB Result;
Result.R.V = A.R.V * Scalar;
Result.G.V = A.G.V * Scalar;
Result.B.V = A.B.V * Scalar;
return Result;
}
FTwoBandSHVectorRGB MulSH(FTwoBandSHVector A, half3 Color)
{
FTwoBandSHVectorRGB Result;
Result.R.V = A.V * Color.r;
Result.G.V = A.V * Color.g;
Result.B.V = A.V * Color.b;
return Result;
}
FTwoBandSHVector MulSH(FTwoBandSHVector A, half Scalar)
{
FTwoBandSHVector Result;
Result.V = A.V * Scalar;
return Result;
}
FThreeBandSHVectorRGB MulSH3(FThreeBandSHVector A, half3 Color)
{
FThreeBandSHVectorRGB Result;
Result.R.V0 = A.V0 * Color.r;
Result.R.V1 = A.V1 * Color.r;
Result.R.V2 = A.V2 * Color.r;
Result.G.V0 = A.V0 * Color.g;
Result.G.V1 = A.V1 * Color.g;
Result.G.V2 = A.V2 * Color.g;
Result.B.V0 = A.V0 * Color.b;
Result.B.V1 = A.V1 * Color.b;
Result.B.V2 = A.V2 * Color.b;
return Result;
}
FThreeBandSHVector MulSH3(FThreeBandSHVector A, half Scalar)
{
FThreeBandSHVector Result;
Result.V0 = A.V0 * Scalar;
Result.V1 = A.V1 * Scalar;
Result.V2 = A.V2 * Scalar;
return Result;
}
FTwoBandSHVector AddSH(FTwoBandSHVector A, FTwoBandSHVector B)
{
FTwoBandSHVector Result = A;
Result.V += B.V;
return Result;
}
FTwoBandSHVectorRGB AddSH(FTwoBandSHVectorRGB A, FTwoBandSHVectorRGB B)
{
FTwoBandSHVectorRGB Result;
Result.R = AddSH(A.R, B.R);
Result.G = AddSH(A.G, B.G);
Result.B = AddSH(A.B, B.B);
return Result;
}
FThreeBandSHVector AddSH(FThreeBandSHVector A, FThreeBandSHVector B)
{
FThreeBandSHVector Result = A;
Result.V0 += B.V0;
Result.V1 += B.V1;
Result.V2 += B.V2;
return Result;
}
FThreeBandSHVectorRGB AddSH(FThreeBandSHVectorRGB A, FThreeBandSHVectorRGB B)
{
FThreeBandSHVectorRGB Result;
Result.R = AddSH(A.R, B.R);
Result.G = AddSH(A.G, B.G);
Result.B = AddSH(A.B, B.B);
return Result;
}
/**
* Computes the dot product of two SH projected scalar functions, giving a scalar value that is
* the integral over all directions of the product of the functions.
*/
half DotSH(FTwoBandSHVector A,FTwoBandSHVector B)
{
half Result = dot(A.V, B.V);
return Result;
}
/**
* Computes the dot product of a SH projected RGB function and a SH projected scalar function,
* giving a RGB value that is the integral over all directions of the product of the functions.
*/
half3 DotSH(FTwoBandSHVectorRGB A,FTwoBandSHVector B)
{
half3 Result = 0;
Result.r = DotSH(A.R,B);
Result.g = DotSH(A.G,B);
Result.b = DotSH(A.B,B);
return Result;
}
half DotSH1(FOneBandSHVector A,FOneBandSHVector B)
{
half Result = A.V * B.V;
return Result;
}
half3 DotSH1(FOneBandSHVectorRGB A,FOneBandSHVector B)
{
half3 Result = 0;
Result.r = DotSH1(A.R,B);
Result.g = DotSH1(A.G,B);
Result.b = DotSH1(A.B,B);
return Result;
}
half DotSH3(FThreeBandSHVector A,FThreeBandSHVector B)
{
half Result = dot(A.V0, B.V0);
Result += dot(A.V1, B.V1);
Result += A.V2 * B.V2;
return Result;
}
half3 DotSH3(FThreeBandSHVectorRGB A,FThreeBandSHVector B)
{
half3 Result = 0;
Result.r = DotSH3(A.R,B);
Result.g = DotSH3(A.G,B);
Result.b = DotSH3(A.B,B);
return Result;
}
FTwoBandSHVector GetLuminance(FTwoBandSHVectorRGB InRGBVector)
{
half3 LF = (half3) LuminanceFactors();
FTwoBandSHVector Out;
Out.V = InRGBVector.R.V * LF.x + InRGBVector.G.V * LF.y + InRGBVector.B.V * LF.z;
return Out;
}
/** Compute the direction which the spherical harmonic is highest at. */
float3 GetMaximumDirection(FTwoBandSHVector SHVector)
{
// This is an approximation which only takes into account first and second order spherical harmonics.
float3 MaxDirection = float3(-SHVector.V.w, -SHVector.V.y, SHVector.V.z);
float Length = length(MaxDirection);
return MaxDirection / max(Length, .0001f);
}
/** Computes the value of the SH basis functions for a given direction. */
FOneBandSHVector SHBasisFunction1()
{
FOneBandSHVector Result;
// These are derived from simplifying SHBasisFunction in C++
Result.V = 0.282095f;
return Result;
}
FTwoBandSHVector SHBasisFunction(half3 InputVector)
{
FTwoBandSHVector Result;
// These are derived from simplifying SHBasisFunction in C++
Result.V.x = 0.282095f;
Result.V.y = -0.488603f * InputVector.y;
Result.V.z = 0.488603f * InputVector.z;
Result.V.w = -0.488603f * InputVector.x;
return Result;
}
FThreeBandSHVector SHBasisFunction3(half3 InputVector)
{
FThreeBandSHVector Result;
// These are derived from simplifying SHBasisFunction in C++
Result.V0.x = 0.282095f;
Result.V0.y = -0.488603f * InputVector.y;
Result.V0.z = 0.488603f * InputVector.z;
Result.V0.w = -0.488603f * InputVector.x;
half3 VectorSquared = InputVector * InputVector;
Result.V1.x = 1.092548f * InputVector.x * InputVector.y;
Result.V1.y = -1.092548f * InputVector.y * InputVector.z;
Result.V1.z = 0.315392f * (3.0f * VectorSquared.z - 1.0f);
Result.V1.w = -1.092548f * InputVector.x * InputVector.z;
Result.V2 = 0.546274f * (VectorSquared.x - VectorSquared.y);
return Result;
}
/** Simplified to only return the ambient coefficient as all the rest are 0. */
half SHAmbientFunction()
{
return 1 / (2 * sqrt(PI));
}
/**
* Computes the diffuse transfer function for a surface with the given normal and DiffusePower,
* and projects it into the SH basis.
*/
FOneBandSHVector CalcDiffuseTransferSH1(half Exponent)
{
FOneBandSHVector Result = SHBasisFunction1();
// These formula are scaling factors for each SH band that convolve a SH with the circularly symmetric function
// max(0,cos(theta))^Exponent
half L0 = 2 * PI / (1 + 1 * Exponent );
// Multiply the coefficients in each band with the appropriate band scaling factor.
Result.V *= L0;
return Result;
}
FTwoBandSHVector CalcDiffuseTransferSH(half3 Normal,half Exponent)
{
FTwoBandSHVector Result = SHBasisFunction(Normal);
// These formula are scaling factors for each SH band that convolve a SH with the circularly symmetric function
// max(0,cos(theta))^Exponent
half L0 = 2 * PI / (1 + 1 * Exponent );
half L1 = 2 * PI / (2 + 1 * Exponent );
// Multiply the coefficients in each band with the appropriate band scaling factor.
Result.V.x *= L0;
Result.V.yzw *= L1;
return Result;
}
FThreeBandSHVector CalcDiffuseTransferSH3(half3 Normal,half Exponent)
{
FThreeBandSHVector Result = SHBasisFunction3(Normal);
// These formula are scaling factors for each SH band that convolve a SH with the circularly symmetric function
// max(0,cos(theta))^Exponent
half L0 = 2 * PI / (1 + 1 * Exponent );
half L1 = 2 * PI / (2 + 1 * Exponent );
half L2 = Exponent * 2 * PI / (3 + 4 * Exponent + Exponent * Exponent);
half L3 = (Exponent - 1) * 2 * PI / (8 + 6 * Exponent + Exponent * Exponent);
// Multiply the coefficients in each band with the appropriate band scaling factor.
#if VULKAN_PROFILE
// Workaround bug in Vulkan precision on mobile in OpVectorTimesScalar
Result.V0.x *= float(L0);
Result.V0.yzw *= float(L1);
Result.V1.xyzw *= float(L2);
Result.V2 *= float(L2);
#else
Result.V0.x *= L0;
Result.V0.yzw *= L1;
Result.V1.xyzw *= L2;
Result.V2 *= L2;
#endif
return Result;
}
/**
* Calculates Irradiance for a cone with the given SH lighting
* Based on "Practical Real-Time Strategies for Accurate Indirect Occlusion"
*/
float3 EvaluateSHIrradiance(float3 Direction, float AO, FThreeBandSHVectorRGB SH)
{
FThreeBandSHVector DiffuseTransferSH = CalcDiffuseTransferSH3(Direction, /*Exponent*/ 1.0f);
// Cos/Sin of a visibility cone aperture angle
float CosA = sqrt(saturate(1.0f - AO));
float SinA = sqrt(saturate(1.0f - CosA * CosA));
// Zonal harmonics expansion
float Z0 = SinA * SinA;
float Z1 = (1.0f - CosA * CosA * CosA);
float Z2 = SinA * SinA * (1.0f + 3.0f * CosA * CosA);
DiffuseTransferSH.V0.x *= Z0;
DiffuseTransferSH.V0.yzw *= Z1;
DiffuseTransferSH.V1.xyzw *= Z2;
DiffuseTransferSH.V2 *= Z2;
return max(float3(0,0,0), DotSH3(SH, DiffuseTransferSH));
}