I am trying to use a render target as displacement for my ocean but I am having precision problems. Specifically, despite having a material that should be producing values varying from 0-512 on a 512x512 texture, I am only getting 160 unique values. Where is the precision being lost and what can I do to avoid it?
Setup
Firstly, I have the following material:
The texture coordinate X is scaled up by 512 and then used as the blue channel of an emissive colour. Therefore, the values should vary from 0-512. Note FFloat16 is perfectly accurate enough to store every whole number in this range accurately and certainly 512 unique values in this range.
This material is then applied to a material billboard component where the BaseSizeX/BaseSizeY are set to 512.
Then a SceneCaptureComponent, positioned 512 units away with a 90 degree FOV (so it captures the whole billboard and nothing else). This renders to a render target in HDR (with otherwise default settings). The render target is HDR and 512x512 in size.
Results
At this point, I’d expect the target to contain 512 unique values ({~0, ~1,…~512}). However, using the following test code it can be seen to have only 160 unique colours. Further, saving it out to a bitmap (after scaling down to fit in the 0-1 range required of a non-HDR image) reveals bands of the same colour (should get 255 different colours):
Test code
FTextureRenderTarget2DResource* textureResource = (FTextureRenderTarget2DResource*)RenderTarget->Resource;
TArray<FFloat16Color> Pixels;
bool bDidRead = textureResource->ReadFloat16Pixels(Pixels)
TSet<float> UniqueColours;
for (FFloat16Color& Colour : Pixels)
{
float Height = Colour.B.GetFloat();
UniqueColours.Add(Height);
}
UE_LOG(LogTemp, Log, TEXT("Unique colours: %i"), UniqueColours.Num());
[Numbers from the UniqueColours set][3]