195 lines
4.7 KiB
C++
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
|