Files
UnrealEngine/Engine/Source/Developer/NaniteUtilities/Public/DisplacementMap.h
2025-05-18 13:04:45 +08:00

140 lines
3.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Engine/Texture.h"
namespace Nanite
{
class FDisplacementMap
{
public:
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;
NANITEUTILITIES_API FVector2f Sample( FVector2f MinUV, FVector2f MaxUV ) 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;
private:
TArray64< uint8 > SourceData;
TArray< FVector2f > MipData[12];
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 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;
}
}
} // namespace Nanite