Files
UnrealEngine/Engine/Plugins/Enterprise/MDLImporter/Shaders/Private/FlakesAccess.ush
2025-05-18 13:04:45 +08:00

138 lines
4.8 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/**
Flakes texture access based on light angular direction, uses a pseduo(i.e. in atlas format) 2D texture array
to access flakes slices and finally applies bilinear interpolation.
@param ThetaAngles - normalized(by Pi/2) thetaF and thetaI angles
@param FlakesTexArray - texture 2d array in an atlas format
@param FlakesTexCount - number of textures in the array
@param FlakesTexValues - x = texture size of an entry in the 2d array, y = ThetaSliceLUT texel size
@param TexCoord - UV coordinates
@param Tiling - UV tiling factor
@param NumThetaF - number of ThetaF bins
@param NumThetaI- number of thetaI bins
@param MaxThetaI - max thetaI bin that is used
@param ThetaSliceLUT - 1d texture with the index into flake texture array for a given thetaI bin
@return Bilinear interpolated value of the flakes.
*/
#define PI2 1.5707963267948966
#define USE_GRADIENTS 1
// NOTE. had to use macros as function structs aren't portable on certain platforms such as Vulkan
#if USE_GRADIENTS == 1
#define SAMPLE_TEX_COORD(SampleTexCoord, Result) \
float2 DdxUV = DDX(SampleTexCoord); \
float2 DdyUV = DDY(SampleTexCoord); \
Result = Texture2DSampleGrad(FlakesTexArray, FlakesTexArraySampler, SampleTexCoord, DdxUV, DdyUV);
#else
Result = Texture2DSample(FlakesTexArray, FlakesTexArraySampler, SampleTexCoord);
#endif
#define SAMPLE_TEX_ARRAY(UV, Index, Result) \
{ \
float SideCount = sqrt(FlakesTexCount); \
float InvSideCount = 1.0 / SideCount; \
float TexPadding = 8.0; \
float TexSize = FlakesTexValues.x; \
float PaddedTexSize = TexSize + TexPadding * 2.0; \
float AtlasTexelSize = 1.0 / (SideCount * PaddedTexSize); \
\
float2 TexOffset = Index; \
TexOffset.x = fmod(TexOffset.x, SideCount); \
TexOffset.y = floor(TexOffset.y * InvSideCount); \
TexOffset *= InvSideCount; \
TexOffset += TexPadding * AtlasTexelSize; \
\
float2 SampleTexCoord = frac(UV); \
SampleTexCoord *= InvSideCount * (TexSize / PaddedTexSize); \
SampleTexCoord += TexOffset; \
SAMPLE_TEX_COORD(SampleTexCoord, Result); \
}
#define SAMPLE_LUT(BinThetaI, Result) \
{ \
float TexelSize = FlakesTexValues.y; \
float Padding = 4.0; \
float Index = (BinThetaI) * Padding * TexelSize + TexelSize; \
float Value = Texture2DSampleLevel(ThetaSliceLUT, ThetaSliceLUTSampler, float2(Index, 0.0), 0.0).x; \
Result = floor(Value * 255.0 + 0.5); \
}
//ThetaF sampling defines the angular sampling, i.e. angular flake lifetime
float ThetaF = clamp(ThetaAngles.x / PI2, 0.0, 1.0) * NumThetaF + 0.5;
float ThetaI = clamp(ThetaAngles.y / PI2, 0.0, 1.0) * NumThetaF + 0.5;
//bilinear interpolation indices
float LowThetaF = floor(ThetaF);
float HighThetaF = LowThetaF + 1.0;
float LowThetaI = floor(ThetaI);
float HighThetaI = LowThetaI + 1.0;
float2 UV = TexCoord * Tiling;
float2 DdxUV = DDX(UV);
float2 DdyUV = DDY(UV);
if (NumThetaI < NumThetaF)
{
if ((LowThetaI % 2) == 1 || (HighThetaI % 2) == 1)
{
//tweak for extra randomness, in the case of almost planar geometries with repeating patches
UV.xy = UV.yx;
}
//map to the original sampling
float InvNumThetaF = 1.0 / NumThetaF;
LowThetaI = int(floor(float(LowThetaI) * NumThetaI * InvNumThetaF));
HighThetaI = int(floor(float(HighThetaI) * NumThetaI * InvNumThetaF));
}
half3 ColorBottomLeft = 0.0;
half3 ColorTopLeft = 0.0;
half3 ColorBottomRight = 0.0;
half3 ColorTopRight = 0.0;
//access flakes texture and stay in the correct slices(no slip over)
if (LowThetaI < MaxThetaI)
{
float BaseThetaIndex, UpperIndexLimit;
SAMPLE_LUT(LowThetaI, BaseThetaIndex);
SAMPLE_LUT(LowThetaI + 1.0, UpperIndexLimit);
float LowThetaIndex = BaseThetaIndex + LowThetaF;
if (LowThetaIndex < UpperIndexLimit)
{
SAMPLE_TEX_ARRAY(UV, LowThetaIndex, ColorBottomLeft.xyz);
}
float HighThetaIndex = BaseThetaIndex + HighThetaF;
if (HighThetaIndex < UpperIndexLimit)
{
SAMPLE_TEX_ARRAY(UV, HighThetaIndex, ColorBottomRight.xyz);
}
}
if (HighThetaI < MaxThetaI)
{
float BaseThetaIndex, UpperIndexLimit;
SAMPLE_LUT(HighThetaI, BaseThetaIndex);
SAMPLE_LUT(HighThetaI + 1.0, UpperIndexLimit);
float LowThetaIndex = BaseThetaIndex + LowThetaF;
if (LowThetaIndex < UpperIndexLimit)
{
SAMPLE_TEX_ARRAY(UV, LowThetaIndex, ColorTopLeft.xyz);
}
float HighThetaIndex = BaseThetaIndex + HighThetaF;
if (HighThetaIndex < UpperIndexLimit)
{
SAMPLE_TEX_ARRAY(UV, HighThetaIndex, ColorTopRight.xyz);
}
}
//bilinear interpolation
float DiffThetaF = ThetaF - float(LowThetaF);
float DiffThetaI = ThetaI - float(LowThetaI);
half3 Bottom = (1.0 - DiffThetaF) * ColorBottomLeft + DiffThetaF * ColorBottomRight;
half3 Top = (1.0 - DiffThetaF) * ColorTopLeft + DiffThetaF * ColorTopRight;
return (1.0 - DiffThetaI) * Bottom + DiffThetaI * Top;