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

171 lines
7.0 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
HeightfieldLightingShared.usf
=============================================================================*/
Texture2D HeightfieldTexture;
SamplerState HeightfieldSampler;
Texture2D DiffuseColorTexture;
SamplerState DiffuseColorSampler;
Texture2D VisibilityTexture;
SamplerState VisibilitySampler;
uint NumHeightfields;
Buffer<float4> HeightfieldDescriptions;
// In float4's, must match C++
#define HEIGHTFIELD_DATA_STRIDE 12
float4 GetHeightfieldScaleBias(uint Index, out bool bHasSubsections)
{
float4 HeightfieldVector = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 0];
// Sign bit stored presence of subsections
bHasSubsections = HeightfieldVector.x < 0;
HeightfieldVector.x = abs(HeightfieldVector.x);
return HeightfieldVector;
}
float4 GetHeightfieldMinMaxUV(uint Index)
{
return HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 1];
}
float2 GetHeightfieldSize(uint Index)
{
return HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 2].xy;
}
float2 GetHeightfieldSizeInv(uint Index)
{
return HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 2].zw;
}
float3x3 GetWorldToLocalRotT(uint Index)
{
float4 M0 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 4];
float4 M1 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 5];
float4 M2 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 6];
float3x3 RelativeWorldToLocal = float3x3(M0.xyz, M1.xyz, M2.xyz);
return RelativeWorldToLocal;
}
FDFInverseMatrix GetWorldToLocal(uint Index)
{
float3 PositionHigh = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 3].xyz;
float4 M0 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 4];
float4 M1 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 5];
float4 M2 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 6];
float4x4 RelativeWorldToLocal = transpose(float4x4(M0, M1, M2, float4(0.0f, 0.0f, 0.0f, 1.0f)));
return MakeDFInverseMatrix4x3(PositionHigh, RelativeWorldToLocal);
}
FDFMatrix GetLocalToWorld(uint Index)
{
float3 PositionHigh = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 3].xyz;
float4 M0 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 7];
float4 M1 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 8];
float4 M2 = HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 9];
float4x4 LocalToRelativeWorld = transpose(float4x4(M0, M1, M2, float4(0.0f, 0.0f, 0.0f, 1.0f)));
return MakeDFMatrix(PositionHigh, LocalToRelativeWorld);
}
float4 GetVisibilityMask(uint Index)
{
return HeightfieldDescriptions[Index * HEIGHTFIELD_DATA_STRIDE + 10];
}
uint GetHeightfieldGPUSceneInstanceIndex(uint HeightfieldIndex)
{
float4 V0 = HeightfieldDescriptions[HeightfieldIndex * HEIGHTFIELD_DATA_STRIDE + 11];
return asuint(V0.x);
}
float GetHeightfieldInvMaxScale(uint HeightfieldIndex)
{
float4 V0 = HeightfieldDescriptions[HeightfieldIndex * HEIGHTFIELD_DATA_STRIDE + 11];
return V0.y;
}
float2 GetHeightfieldUV(uint HeightfieldIndex, float2 LocalPosition, out float4 MinMaxHeightfieldUV)
{
bool bHasSubsections;
float4 HeightfieldScaleBias = GetHeightfieldScaleBias(HeightfieldIndex, bHasSubsections);
float2 HeightfieldUV = LocalPosition * HeightfieldScaleBias.xy + HeightfieldScaleBias.zw;
MinMaxHeightfieldUV = GetHeightfieldMinMaxUV(HeightfieldIndex);
if (bHasSubsections)
{
float2 CenterUV = .5f * (MinMaxHeightfieldUV.xy + MinMaxHeightfieldUV.zw);
// One row of heights in the heightfield are duplicated between subsections
// The only number of subsections supported is 2
// If we are in subsection 0, no adjustment is needed, but if we are in subsection 1 we need to shift up by one texel
HeightfieldUV += select((HeightfieldUV + .5f * HeightfieldScaleBias.xy) > CenterUV, HeightfieldScaleBias.xy, 0.0);
}
return HeightfieldUV;
}
float2 GetHeightfieldUV(uint HeightfieldIndex, float2 LocalPosition)
{
float4 MinMaxHeightfieldUV;
return GetHeightfieldUV(HeightfieldIndex, LocalPosition, MinMaxHeightfieldUV);
}
float2 GetVisibilityUV(uint HeightfieldIndex, float2 LocalPosition)
{
return LocalPosition * GetHeightfieldSizeInv(HeightfieldIndex);
}
float SampleHeightfield(uint HeightfieldIndex, float4 HeightfieldScaleBias, float2 LocalPosition)
{
float4 MinMaxHeightfieldUV;
float2 HeightfieldUV = GetHeightfieldUV(HeightfieldIndex, LocalPosition, MinMaxHeightfieldUV);
HeightfieldUV = clamp(HeightfieldUV, MinMaxHeightfieldUV.xy, MinMaxHeightfieldUV.zw);
return DecodePackedHeight(Texture2DSampleLevel(HeightfieldTexture, HeightfieldSampler, HeightfieldUV + 0.5f * HeightfieldScaleBias.xy, 0).xy);
}
FDFVector3 GetHeightfieldWorldPositionAndNormal(uint HeightfieldIndex, float2 LocalPosition, float2 HeightfieldUV, float CentralDiffOffset, out float3 WorldNormal, out float Visibility)
{
//@todo - don't call GetHeightfieldScaleBias twice
bool bHasSubsections;
float4 HeightfieldScaleBias = GetHeightfieldScaleBias(HeightfieldIndex, bHasSubsections);
float4 SampleValue = Texture2DSampleLevel(HeightfieldTexture, HeightfieldSampler, HeightfieldUV + .5f * HeightfieldScaleBias.xy, 0);
float HeightfieldHeight = DecodePackedHeight(SampleValue.xy);
float2 SampleNormal = float2(SampleValue.b, SampleValue.a) * float2(2.0,2.0) - float2(1.0,1.0);
float3 HeightfieldNormal = float3(SampleNormal, sqrt(max(1.0 - dot(SampleNormal, SampleNormal), 0.0)));
// Reconstruct normal using central differences
// This is usually a better approximation for normal over the CentralDiffOffset area than SampleNormal
{
float2 TexelOffset = CentralDiffOffset * GetHeightfieldInvMaxScale(HeightfieldIndex);
float R = SampleHeightfield(HeightfieldIndex, HeightfieldScaleBias, LocalPosition.xy + float2(+TexelOffset.x, 0.0f));
float L = SampleHeightfield(HeightfieldIndex, HeightfieldScaleBias, LocalPosition.xy + float2(-TexelOffset.x, 0.0f));
float T = SampleHeightfield(HeightfieldIndex, HeightfieldScaleBias, LocalPosition.xy + float2(0.0f, +TexelOffset.y));
float B = SampleHeightfield(HeightfieldIndex, HeightfieldScaleBias, LocalPosition.xy + float2(0.0f, -TexelOffset.y));
float3 HeightfieldGradient = float3((R - L) / (2.0f * TexelOffset.x), (T - B) / (2.0f * TexelOffset.y), 1.0f);
float HeightfieldGradientLength = length(HeightfieldGradient);
HeightfieldNormal = HeightfieldGradientLength > 0.001f ? HeightfieldGradient / HeightfieldGradientLength : HeightfieldNormal;
}
WorldNormal = normalize(mul(HeightfieldNormal, GetWorldToLocalRotT(HeightfieldIndex)));
float2 VisibilityUV = GetVisibilityUV(HeightfieldIndex, LocalPosition);
float4 SampleVisibility = Texture2DSampleLevel(VisibilityTexture, VisibilitySampler, VisibilityUV, 0);
Visibility = 1.f - dot(SampleVisibility, GetVisibilityMask(HeightfieldIndex));
float3 HeightfieldLocalPosition = float3(LocalPosition, HeightfieldHeight);
FDFVector3 HeightfieldWorldPosition = DFMultiply(HeightfieldLocalPosition, GetLocalToWorld(HeightfieldIndex));
return HeightfieldWorldPosition;
}