Download

How should I sample a runtime-generated texture in all my game's materials?

Currently, I am writing to a dynamic texture at run-time, using


UTexture2D::CreateTransient

I want to use this texture in all my game’s materials.
The only way I know, is to assign this dynamic texture to a material at run-time, by creating a new dynamic material instance at run-time and setting a texture parameter value on it to reference my dynamic texture. To do this for all materials in my game would be tricky/not a good solution.

Can anyone see a better solution?

I did consider writing to a renderTarget instead of a Transient Utexture. In the material editor, I can use a render targets same way it do normal textures. However Im not sure how to write to an existing renderTarget at run-time (efficiently).

Dynamic Material Instances are the only option if you are creating the texture at runtime. Alternatively, you can create a render target asset as you say and have all materials point to that. That’s arguably the most efficient way for a very widely-used texture source.

In terms of updating the render target, it’s no different to any other texture. You can still resize it and draw stuff into it however you like. You just need a reference to it, the same way as any other asset.

As for updating it, there are a few different ways - but I guess it depends on what you’re doing. UE has the “Draw Material To Render Target” system which is pretty nice, or if you want to write pixel values directly, you can use a render command.

Here’s a copy-paste from some of my gamecode. In this case, this is a HDR Render Target and ‘RenderData’ is a TArray<FFloat16Color> of equal size, and matching memory layout. I’m quite literally copying the array into the render target.



if (RenderTarget
&& RenderTarget->Resource
&& RenderTarget->Resource->TextureRHI.IsValid())
{
const uint32 NumBytes = CalculateImageBytes(RenderTarget->SizeX, RenderTarget->SizeY, 0, RenderTarget->GetFormat());

ENQUEUE_RENDER_COMMAND(FHT_ShieldRenderData)(
[Data = RenderData, Bytes = NumBytes, LUT = RenderTarget->Resource->TextureRHI, bFlush = bFlushRendering](FRHICommandListImmediate& RHICmdList)
{
uint32 DestStride = 0;
FFloat16Color* DestBuffer = static_cast<FFloat16Color*>(RHILockTexture2D(LUT->GetTexture2D(), 0, RLM_WriteOnly, DestStride, false, false));

FMemory::Memcpy(DestBuffer, Data.GetData(), Bytes);
RHIUnlockTexture2D(LUT->GetTexture2D(), 0, false, bFlush);
});

return true;
}


Be careful what you pass between render and game thread however. I’m passing data by copying it here, but if you want to reference an object on the render thread or something, you need to use a render fence or somesuch:

https://docs.unrealengine.com/en-US/…ing/index.html

The code above is pretty fast, I’ve not had any issues doing this yet. The real slowdowns come when you do something that calls FlushRenderingCommands(), which blocks Game Thread execution until the rendering thread is done. It’s not really acceptable to use that outside of the editor environment.

Thanks So much TheJamsh

I have some errors though, could you tell me whats wrong? Sorry, im not a programmer.

Looks like you’re just missing the bFlushRendering argument (in my case it was a function param) - you can probably remove it, and just replace bFlush with false.

Just be aware that the types you use may differ depending on the format of the render target - if you do want to use memcpy, you have to be certain that your data and the render target data are the same size, layout and format.