Files
UnrealEngine/Engine/Source/Developer/NaniteUtilities/Public/DisplacementMap.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

195 lines
4.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Engine/Texture.h"
#include "Serialization/Archive.h"
class FArchive;
namespace Nanite
{
class FDisplacementMap
{
private:
static constexpr size_t MAX_NUM_MIP_LEVELS = 13;
public:
virtual ~FDisplacementMap() {}
ETextureSourceFormat SourceFormat;
int32 BytesPerPixel;
int32 SizeX;
int32 SizeY;
uint32 NumLevels;
float Magnitude;
float Center;
TextureAddress AddressX;
TextureAddress AddressY;
public:
NANITEUTILITIES_API FDisplacementMap();
NANITEUTILITIES_API FDisplacementMap( struct FImage&& TextureSourceImage, float InMagnitude, float InCenter, TextureAddress InAddressX, TextureAddress InAddressY );
// Bilinear filtered
NANITEUTILITIES_API float Sample( FVector2f UV ) const;
// Bounds over UV-rectangle (approximate, but conservative)
NANITEUTILITIES_API FVector2f Sample( FVector2f MinUV, FVector2f MaxUV ) const;
// Bounds over UV-rectangle (hierarchical traversal, increased refinements lead to tighter bounds,
// but significantly more expensive for large numbers of refinements)
NANITEUTILITIES_API FVector2f SampleHierarchical( FVector2f MinUV, FVector2f MaxUV, uint32 MaxRefinements ) const;
// hierarchical sample warping for perfect importance sampling according to probability density represented by image (not required to be normalized).
// [ Clarberg, et al., "Wavelet importance sampling: efficiently evaluating products of complex functions" ]
NANITEUTILITIES_API FVector2f WarpSample(const FVector2f& UV) const;
/**
* Filtered with elliptic weighted averaging
*
* \param Axis0 major axis
* \param Axis1 minor axis
*/
NANITEUTILITIES_API float SampleEWA( FVector2f UV, FVector2f Axis0, FVector2f Axis1 ) const;
float Sample( int32 x, int32 y ) const;
FVector2f Sample( int32 x, int32 y, uint32 Level ) const;
float Load( int32 x, int32 y ) const;
FVector2f Load( int32 x, int32 y, uint32 Level ) const;
float LoadFiltered( int32 x, int32 y, uint32 Level ) const;
NANITEUTILITIES_API virtual void Serialize(FArchive& Ar);
private:
// per-level EWA
float EWA( uint32 Level, FVector2f UV, FVector2f Axis0, FVector2f Axis1 ) const;
TArray64< uint8 > SourceData;
TArray< FVector2f > MipData[ MAX_NUM_MIP_LEVELS ];
TArray< float > MipDataFiltered[ MAX_NUM_MIP_LEVELS ];
void Address( int32& x, int32& y ) const;
};
FORCEINLINE float FDisplacementMap::Sample( int32 x, int32 y ) const
{
Address( x, y );
float Displacement = Load( x, y );
Displacement -= Center;
Displacement *= Magnitude;
return Displacement;
}
FORCEINLINE FVector2f FDisplacementMap::Sample( int32 x, int32 y, uint32 Level ) const
{
Address( x, y );
x >>= Level;
y >>= Level;
FVector2f Displacement = Load( x, y, Level );
Displacement -= FVector2f( Center );
Displacement *= Magnitude;
return Displacement;
}
FORCEINLINE float FDisplacementMap::Load( int32 x, int32 y ) const
{
const uint8* PixelPtr = &SourceData[ int64( x + (int64)y * SizeX ) * BytesPerPixel ];
if( SourceFormat == TSF_BGRA8 )
{
return float( PixelPtr[2] ) / 255.0f;
}
else if( SourceFormat == TSF_RGBA16 )
{
checkSlow( BytesPerPixel == sizeof(uint16) * 4 );
return float( *(uint16*)PixelPtr ) / 65535.0f;
}
else if( SourceFormat == TSF_RGBA16F || SourceFormat == TSF_R16F )
{
FFloat16 HalfValue = *(FFloat16*)PixelPtr;
return HalfValue;
}
else if( SourceFormat == TSF_G8 )
{
return float( PixelPtr[0] ) / 255.0f;
}
else if( SourceFormat == TSF_G16 )
{
return float( *(uint16*)PixelPtr ) / 65535.0f;
}
else if( SourceFormat == TSF_RGBA32F || SourceFormat == TSF_R32F )
{
return *(float*)PixelPtr;
}
else
{
checkf( 0, TEXT("Displacement map format not supported") );
return 0.0f;
}
}
FORCEINLINE FVector2f FDisplacementMap::Load( int32 x, int32 y, uint32 Level ) const
{
checkSlow( Level > 0 );
uint32 MipSizeX = ( ( SizeX - 1 ) >> Level ) + 1;
uint32 MipSizeY = ( ( SizeY - 1 ) >> Level ) + 1;
return MipData[ Level - 1 ][ x + y * MipSizeX ];
}
FORCEINLINE float FDisplacementMap::LoadFiltered( int32 x, int32 y, uint32 Level ) const
{
checkSlow( Level >= 0 );
if( Level == 0 )
{
return Load( x, y );
}
uint32 MipSizeX = ( ( SizeX - 1 ) >> Level ) + 1;
uint32 MipSizeY = ( ( SizeY - 1 ) >> Level ) + 1;
return MipDataFiltered[ Level - 1 ][ x + y * MipSizeX ];
}
FORCEINLINE void FDisplacementMap::Address( int32& x, int32& y ) const
{
if( AddressX == TA_Clamp )
x = FMath::Clamp( x, 0, SizeX - 1 );
else
{
x = x % SizeX;
x += x < 0 ? SizeX : 0;
}
if( AddressY == TA_Clamp )
y = FMath::Clamp( y, 0, SizeY - 1 );
else
{
y = y % SizeY;
y += y < 0 ? SizeY : 0;
}
}
inline FArchive& operator<<(FArchive& Ar, FDisplacementMap& DisplacementMap)
{
DisplacementMap.Serialize(Ar);
return Ar;
}
} // namespace Nanite