Best way to apply a shader to certain controls and children

Godot Version

4.4.1

Question

I have created a couple of canvas_item shaders which I’m currently applying to the enter screen as a post process using BackBufferCopy nodes to allow me to do 2 separate passes.

However, I don’t want to apply the shader to the entire scene only to certain areas which are defined by Control nodes, for example the area of the screen covered by a container.

I first tried setting up a SubViewport and a TextureRect as a render target and while this works there were several drawbacks:

  • I can only do one pass not two
  • The configuration of a SubViewportContainer, SubViewport, TextureRect, setting Texture to GetTexture in a script etc. meant it was hard to reuse and fiddly to configure
  • This doesn’t scale well to 10s of controls on the screen at once

Next I thought perhaps I can use a stencil buffer to stencil out the regions for post processing but it seems like they aren’t supported in canvas_item shaders and I’m still not sure how I could do this anyway without Subviewports.

Is it possible to do what I’m attempting and if so how could it be done?

Why can’t you apply the shaders directly to the controls you want to affect?

If I just add a shader under the Controls Canvas Item > Material, it doesn’t seem to work, it also doesn’t allow for multiple passes to be applied.

I managed to solve this in a general purpose enough way using subviewports so I will accept that solution.

If anyone is curious. I made a PostProcessContainer script which I can attach to any node which I want to apply the post processing to. When it runs, it inserts a SubViewportContainer and SubViewport into its parent and moves itself underneath. Then I have a global reference to a composition SubViewport and the script creates a TextureRect inside the composition SubViewport, sets the TextureRect.Texture to the output of the first SubViewport and repositions the TextureRect in _process to mirror the component’s position and size.

Then I chained several “global” SubViewports together so each one applies a shader pass, finally displaying to the screen. The composition/post processing SubViewport are all set to transparent background so they only overwrite the portion of the screen occupied by a control which needs post processing.

It was kind of a hassle to get it all set up but should be quick to add to new controls by simply attaching the PostProcessContainer script.

1 Like

You have to add it to a ShaderMaterial and add that to the Material property, I also personally ad it in code because I also tween shader properties at that time.

Only Spatial (3D) shaders allow multiple passes as far as I know. Though it looks like you’ve found a way around that limitation.