Hey there. I have an issue that bothers me since days now. I’m trying to read pixel data from a UTexture2D, but am not able to get the correct result.
My goal:
For my survival game I want the whole map to have environment parameters like oxygen density, radiation level, soil quailty, etc. I want to have actors (let’s say a plant that generates oxygen) that influence said environment. I also want to have a global “delta” map that applies a predefined amount of values to the environment.
My solution:
- I divided the world into a 2048x2048 grid. For that I am using a 2048x2048 PF_FloatRGBA UTexture2D.
- Each tick (each 1s for now) I read from a second UTexture2D and add its values to the first one. I substruct -0.5f for each value, so my “delta map” can have areas in which for example oxygen is produced and areas in which oxygen is reduced.
- Each tick I iterate through all my UEnvironmentComponents and apply their influences in a given value directly to the environment texture data.
My Problem:
When reading pixel values from my delta texture I get invalid RGBA values. I created a texture in GIMP that has 0.5 in all channels. I saved it as PNG RGBA 16 bit. When running the game, instead of all values being (0.5, 0.5, 0.5, 0.5) they are (0.106994629, 0.106994629, 0.106994629, 0.5).
Let’s check a bit of code.
Initialization:
void UEnvironmentalMapSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
EnvironmentTexture = UTexture2D::CreateTransient(2048, 2048, PF_FloatRGBA, TEXT("EnvironmentTexture"));
EnvironmentMipMap = &EnvironmentTexture->GetPlatformData()->Mips[0];
if (AMyWorldSettings* WorldSettings = Cast<AMyWorldSettings>(GetWorld()->GetWorldSettings()))
{
if (WorldSettings->EnvironmentBase && WorldSettings->EnvironmentBase->GetPixelFormat() == PF_FloatRGBA)
{
InputTexture = WorldSettings->EnvironmentBase;
DeltaMipMap = &InputTexture->GetPlatformData()->Mips[0];
}
}
}
Each tick:
void UEnvironmentalMapSubsystem::ApplyTextureEffects(float DeltaSeconds)
{
if (!DeltaMipMap || !EnvironmentMipMap || DeltaMipMap->SizeX != EnvironmentMipMap->SizeX || DeltaMipMap->SizeY != EnvironmentMipMap->SizeY)
{
return;
}
const FFloat16Color* DeltaTextureData = static_cast<FFloat16Color*>(DeltaMipMap->BulkData.Lock(LOCK_READ_ONLY));
FFloat16Color* EnvironmentTextureData = static_cast<FFloat16Color*>(EnvironmentMipMap->BulkData.Lock(LOCK_READ_WRITE));
const int32 TextureWidth = EnvironmentTexture->GetSizeX();
const int32 TextureHeight = EnvironmentTexture->GetSizeY();
for (int32 y = 0; y < TextureHeight; ++y)
{
for (int32 x = 0; x < TextureWidth; ++x)
{
if (bValid)
{
const int32 Index = y * TextureWidth + x;
// Extract input values
float InputOxygen = DeltaTextureData[Index].R.GetFloat(); // 0.106994629 instead of 0.5
float InputRadiation = DeltaTextureData[Index].G.GetFloat(); // 0.106994629 instead of 0.5
float InputTemperature = DeltaTextureData[Index].B.GetFloat(); // 0.106994629 instead of 0.5
float InputHumidity = DeltaTextureData[Index].A.GetFloat(); // 0.5, correct
// Convert from range [0, 1] to range [-0.5, 0.5]
InputOxygen -= 0.5f;
InputRadiation -= 0.5f;
InputTemperature -= 0.5f;
InputHumidity -= 0.5f;
// Extract existing values
float ExistingOxygen = EnvironmentTextureData[Index].R.GetFloat();
float ExistingRadiation = EnvironmentTextureData[Index].G.GetFloat();
float ExistingTemperature = EnvironmentTextureData[Index].B.GetFloat();
float ExistingHumidity = EnvironmentTextureData[Index].A.GetFloat();
// Apply changes
ExistingOxygen += InputOxygen * DeltaSeconds;
ExistingRadiation += InputRadiation * DeltaSeconds;
ExistingTemperature += InputTemperature * DeltaSeconds;
ExistingHumidity += InputHumidity * DeltaSeconds;
// Clamp values between 0 and 1
ExistingOxygen = FMath::Clamp(ExistingOxygen, 0.0f, 1.0f);
ExistingRadiation = FMath::Clamp(ExistingRadiation, 0.0f, 1.0f);
ExistingTemperature = FMath::Clamp(ExistingTemperature, 0.0f, 1.0f);
ExistingHumidity = FMath::Clamp(ExistingHumidity, 0.0f, 1.0f);
// Update texture data
EnvironmentTextureData[Index].R = FFloat16(ExistingOxygen);
EnvironmentTextureData[Index].G = FFloat16(ExistingRadiation);
EnvironmentTextureData[Index].B = FFloat16(ExistingTemperature);
EnvironmentTextureData[Index].A = FFloat16(ExistingHumidity);
}
}
}
EnvironmentMipMap->BulkData.Unlock();
DeltaMipMap->BulkData.Unlock();
}
Texture settings:
I tried so many things like different texture formats, different encoding override and color space settings. I exported and reimported in various formats (png, tga, tiff, exr) with all possible combinations of export settings I also tried creating the texture in blender instead of GIMP as it has some more export settings, especially for EXR. All of them gives different results, sometimes its 0.106994629, sometimes its 0.2… but none of them gives the correct result of 0.5 in all channels. What am I doing wrong?