How to get texture pixels using UTextureRenderTarget2D class?

I want to get a RenderTarget pixel data real time, so I should to get pixels array pointer(memory address) of RenderTarget.
How can I use UTextureRenderTarget2D class to meet the requirements?

Hey,
Did you find a solution to this?

I used this:

        UTexture2D* Aux2DTex = RenderTexture->ConstructTexture2D(this, "AlphaTex", EObjectFlags::RF_NoFlags, CTF_DeferCompression);
        
        //Make sure it won't be compressed (https://wiki.unrealengine.com/Procedural_Materials#Texture_Setup)
        Aux2DTex->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
        
        //It seems that this field is only available in editor mode
         #if WITH_EDITORONLY_DATA
           Aux2DTex->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
        #endif
        
        //Turn off Gamma-correction
        Aux2DTex->SRGB = 0;
        //Update the texture with new variable values.
        Aux2DTex->UpdateResource();
        
        FColor* FormatedImageData = NULL;

//This function will allocate memory for FormattedImageData pointer and then you can read from here all the pixels info
        Aux2DTex->PlatformData->Mips[0].BulkData.GetCopy((void**)&FormatedImageData);
    
    for (int i = 0; i < ALPHA_MAP_WIDTH * ALPHA_MAP_HEIGHT; i++){
    
          //Do your stuff reading pixels here...
FormatedImageData[i] (Whatever..)
    
    }

The variable “RenderTexture” is a UTextureRenderTarget2D

You must be aware that the ConstructTexture2D function will only work if the render target size is SQUARED and POWER OF TWO.

I hope this code helps you…

EDIT:

It seems that this code only works when you launch the game in edior, the ConstructTexture2D has a compiler macro that makes that function return NULL if the game isn’t lauched from the editor…
I’m working on a new way to do this, if I achieve it I will make you know.

1 Like

thanks for reply, I ignore" the render target size is SQUARED and POWER OF TWO", now I can create texture2D using ConstructTexture2D, thanks again.

Good code.
You’re a genius.
Thank you. ^^

Am I correct that by this method we can access the texture output after matterial getting applied to it ?

This code copies the UTextureRenderTarget2D content into a new UTexture2D and works on published game.

// Creates Texture2D to store TextureRenderTarget content
UTexture2D *Texture = UTexture2D::CreateTransient(TextureRenderTarget->SizeX, TextureRenderTarget->SizeY, PF_B8G8R8A8);
#if WITH_EDITORONLY_DATA
Texture->MipGenSettings = TMGS_NoMipmaps;
#endif
Texture->SRGB = TextureRenderTarget->SRGB;

// Read the pixels from the RenderTarget and store them in a FColor array
TArray<FColor> SurfData;
FRenderTarget *RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixels(SurfData);

// Lock and copies the data between the textures
void* TextureData = Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
const int32 TextureDataSize = SurfData.Num() * 4;
FMemory::Memcpy(TextureData, SurfData.GetData(), TextureDataSize);
Texture->PlatformData->Mips[0].BulkData.Unlock();
// Apply Texture changes to GPU memory
Texture->UpdateResource();

If you only want to access pixels from RenderTarget, just use this part of the code and an index formula.

// Read the pixels from the RenderTarget and store them in a FColor array
TArray<FColor> SurfData;
FRenderTarget *RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixels(SurfData);
// Index formula
FColor PixelColor = SurfData[x + y * TextureRenderTarget->SizeX];
3 Likes

simple and work like a charm

Hi. I am using this (second) technique but all of the pixels I get from ReadPixels are black. Any ideas why? Thanks!

Hey Soame problem here, I get all my pixels black. Any idea why this could happen? Is it areading pixels problem or a configuration of the RenderTarget? Or the SceneCapture2D component? Any ideas?
Can somebody give a full fledged example?

UE4 docs states that it is not allowed to dereference pointer returned by UTextureRenderTarget::GameThread_GetRenderTargetResource. Try to use UTextureRenderTarget::GetRenderTargetResource instead on render thread. Or look at Render Target Lookup aricle.

If you only want to access pixels from
RenderTarget, just use this part of
the code and an index formula.

I tried to use it but how work with x and y for array data? and PixelColor not array - it’s struct of byte rgba

Hello , i really loved that code it does it work but , it crashes my game everytime , if please can help me at this .

Hey! I haven’t seen notifications for this in a while! Wow, my post was on 2016!! Time flies!

This is from the engine code, in 5.2. No better reference than the engine itself, especially since it’s made to work on packaged games too.

There’re functions in UKismetRenderingLibrary for reading pixels from UTextureRenderTarget2D and all of them use the same helper function, called ReadRenderTargetHelper. Unfortunately that function is private, declared and defined in a cpp file, so you’ll have to do one of these:

  • Use the public static functions from the engine, like UKismetRenderingLibrary::ReadRenderTarget, which are also Blueprint accessible (bonus).
  • Copy/paste the code from that helper function. I wouldn’t, since the UKismetRenderingLibrary functions look just fine, passing the pixel values into an array by ref.

It’s important to note that these must be called on the game thread, not the render thread.

If you really want to copy the code from the private function, here goes, without the checks for nullptr and valid sizes and coordinates (don’t forget those!). The function returns the render target pixel format, that’s why there’s the OutFormat variable. If you’re sure of the format you want, you can probably remove a lot of it.

TArray<FColor>& OutLDRValues;
TArray<FLinearColor>& OutHDRValues;

UTextureRenderTarget2D* TextureRenderTarget = /* your texture */;

int32 X; // Give these ints actual values
int32 Y;
int32 Width;
int32 Height;
bool bNormalize = true;

EPixelFormat OutFormat = PF_Unknown;

FTextureRenderTarget2DResource* RTResource = (FTextureRenderTarget2DResource*)TextureRenderTarget->GameThread_GetRenderTargetResource();

FIntRect SampleRect(X, Y, X + Width, Y + Height);

FReadSurfaceDataFlags ReadSurfaceDataFlags = bNormalize ? FReadSurfaceDataFlags() : FReadSurfaceDataFlags(RCM_MinMax);

FRenderTarget* RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource();
OutFormat = TextureRenderTarget->GetFormat();

const int32 NumPixelsToRead = Width * Height;

switch (OutFormat)
{
case PF_B8G8R8A8:
	if (!RenderTarget->ReadPixels(OutLDRValues, ReadSurfaceDataFlags, SampleRect))
	{
		OutFormat = PF_Unknown;
	}
	else
	{
		check(OutLDRValues.Num() == NumPixelsToRead);
	}
	break;
case PF_FloatRGBA:
	if (!RenderTarget->ReadLinearColorPixels(OutHDRValues, ReadSurfaceDataFlags, SampleRect))
	{
		OutFormat = PF_Unknown;
	}
	else
	{
		check(OutHDRValues.Num() == NumPixelsToRead);
	}
	break;
default:
	OutFormat = PF_Unknown;
	break;
}

return OutFormat;

Again: game thread only!!

@RDaneelOlivaw and @LonesomeSoldier
Check if the texture has anything using it in a material or something.
IIRC, the texture may also need bCanCreateUAV to be true for it to be readable on the CPU.

For reading from a render target on the render thread:

UTextureRenderTarget2D* RT;
TArray<FColor>* OutPixels;

// If you're on the game thread (or any non-render thread) you need this to schedule work for the render thread
ENQUEUE_RENDER_COMMAND(ReadRTCmd)(
	[RT, OutPixels](FRHICommandListImmediate& RHICmdList)
{
	// If you're already on the render thread, you just need this
	FTextureRenderTarget2DResource* RTResource =
		static_cast<FTextureRenderTarget2DResource*>(RT->GetRenderTargetResource());
	RHICmdList.ReadSurfaceData(
		RTResource->GetTextureRHI(),
		FIntRect(0, 0, RT->SizeX, RT->SizeY),
		*OutPixels,
		FReadSurfaceDataFlags());
});

// Call this if you need to wait for the read to finish before continuing work on the game thread.
// It's potentially slow though and if you need it, you're probably fine using the UKismetRendererLibrary functions.
FlushRenderingCommands();
1 Like

UE5.1 “RHICmdList.ReadSurfaceData” it is normal in play viewport mode, but it causes the scene to shake in standalone game