How To Convert An FRDGTexture into something that can be used by a Material Expression? It can only take UTextures?

I’m fairly new to Rendering in Unreal Engine & UE5 and so I’m struggling to understand a few things and wondering if anyone would be able to help me.

I’m currently running a number of full-screen passes in C++ and instead of rendering to the screen, I would like to be able to expose the resulting texture from these various passes into something that I can use inside of the Material Editor. Similar to how we can access the SceneTextures (SceneColor, DepthColor etc…)

Here is an example of one of the many passes I’m doing using the GraphBuilder and a custom shader:

FScreenPassRenderTarget ColorTexture;
ColorTexture.Texture = GraphBuilder.CreateTexture(SceneColor.Texture->Desc, TEXT("Scene Color Copy"), ERDGTextureFlags::None);

//Brightness Texture
if (BrightnessPass) {
	// Shader setup
	TShaderMapRef<FTestShaderPS> TestPixelShader(GlobalShaderMap);
	FTestShaderPS::FParameters* TestShaderParameters = GraphBuilder.AllocParameters<FTestShaderPS::FParameters>();
	TestShaderParameters->ViewParams = SceneTextureViewportParams;
	TestShaderParameters->SceneColor = (*Inputs.SceneTextures)->SceneColorTexture;
	TestShaderParameters->SceneColorSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();

	//Render to the screen if we are in preview mode
	if (PreviewPasses) {
		TestShaderParameters->RenderTargets[0] = FRenderTargetBinding(SceneColor.Texture, ERenderTargetLoadAction::ELoad);
	}
	else {
		TestShaderParameters->RenderTargets[0] = ColorTexture.GetRenderTargetBinding();
	}

	TestShaderParameters->Threshold = Threshold;
	TestShaderParameters->Intensity = Intensity;

	FPixelShaderUtils::AddFullscreenPass(
		GraphBuilder,
		GlobalShaderMap,
		FRDGEventName(TEXT("Render Brightness")),
		TestPixelShader,
		TestShaderParameters,
		Viewport);
}

My idea was to expose my ColorTexture property to a Material Expression so that it can be referenced in the Material Editor but it seems as though the Compiler can only take a UTexture/UObject as an input. I can’t seem to find a way to convert the FRDGTexture/Resource into a UTexture. that the material compiler can use.

Does anyone know of a way to achieve what I’m trying to accomplish? Or, perhaps of a better way to expose my resulting texture somehow?

I tried rendering the result directly to a Render Target and just using a standard Texture Input node, but it had a number of issues – so I figured that perhaps exposing this FRDGTexture to the compiler directly would be better.

Worst case scenario, I can do all of my post-processing using the above techniques – but I was really hoping to rely on that as a last resort and only use C++ for things the Material graph can’t do very easily.

Much of this stuff seems quite difficult to find any information on. So any help would be greatly appreciated, thanks!

1 Like

Did you find an answer to this?

Unfortunately render graph/legacy UTexture resources aren’t designed to be dynamically created or resized on the render thread. You had the right idea with copying the shader output to an externally bound render target and then using a texture input node in a Material blueprint to get the texture on the screen. (You might have to do some wizardry to get the render target to wrap correctly on whatever mesh you’re using, but that’s down the road a little bit.)

I’m using a WorldSubsystem derived class to register a SceneViewExtension which dispatches my compute shader pass. The subsystem and its UPROPERTY fields are conveniently accessible from both the SVE and Blueprint. In the view extension, after AddPass(), I have the shader output in a FRDGTextureRef (which you could probably manage depending on how you configure your shader params). Then, I can get a reference to the render target from the subsystem, drill down into its underlying RHI resource (SubsystemTarget->GetRenderTargetResource()), convert that into an RDG reference via pooled render target with GraphBuilder.RegisterExternalTexture(), and finally copy the actual texture with AddCopyTexturePass().

One thing you have to be careful of is to validate the pixel format and screen extent information between your shader output and your render target resource. Specifically, if the pixel format doesn’t match, you’ll get a really obscure crash. The nastiest part about this is having to create and manage the render target on the blueprint side (i.e. on the game thread), requiring that you expose screen dimensions from your view extension out to blueprint and do your render target creation/resizing there. There’s probably a better way to get “primary viewport” extent info in Blueprint or the subsystem, but I haven’t bothered. I can’t post code here, but please ask if you need help.