It took me a while but here is some code to get a still image as a texture at runtime (as of UE5.03). Note that I have made the function async to avoid GPU bottlenecks and because I don’t need the resulting texture immediately. Consider trade-offs when using:
AsyncTask(ENamedThreads::GameThread, [this, Camera]()
{
// "Camera" = 2D Scene Capture Component
if (Camera)
{
// define image resolution
Camera->TextureTarget = UCanvasRenderTarget2D::CreateCanvasRenderTarget2D(this, UCanvasRenderTarget2D::StaticClass(), 512, 512);
Camera->CaptureScene();
// Creates Texture2D to store TextureRenderTarget content
UTexture2D* Texture = UTexture2D::CreateTransient(Camera->TextureTarget->SizeX, Camera->TextureTarget->SizeY);
#if WITH_EDITORONLY_DATA
Texture->MipGenSettings = TMGS_NoMipmaps;
#endif
Texture->SRGB = false;
Texture->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
// Read the pixels from the RenderTarget and store them in a FColor array
TArray<FColor> SurfData;
FRenderTarget* RenderTarget = Camera->TextureTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixels(SurfData);
// some alpha channels are set to 0 by default, make sure all pixels opaque
for (auto& Pixel : SurfData)
{
Pixel.A = 255.f;
}
// Lock and copies the data between the textures
void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
const int32 TextureDataSize = SurfData.Num() * 4;
FMemory::Memcpy(TextureData, SurfData.GetData(), TextureDataSize);
Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
// Apply Texture changes to GPU memory
Texture->UpdateResource();
// Do something with texture
}
});
Hope this saves someone a few hours of research