Custom draw call with different shaders for each texture

Godot Version

v4.7.stable.mono.official [5b4e0cb0f]

Question

I have a CanvasItem (Sprite2D) with a list of overlays that I draw on top of it using DrawTextureRectRegion. However, I’m now adding shaders to some of those textures, so it’s not adequate for my purposes anymore. I need to be able to apply a specific, arbitrary shader to some textures. I’ve been assigning the shader to a ShaderMaterial and configuring it for the overlay, but there’s no obvious way to draw on a CanvasItem with anything but the CanvasItem’s material.

There are several cases where I need to draw with different distinct shader parameters for different overlay textures with the same shader on the same CanvasItem. I had previously avoided making these overlays their own visible Sprite2Ds as children of the CanvasItem for various reasons (for example, you can look at objects, which will display their sprite in a popup; this would require duplicating the entire tree if each overlay had to be its own Sprite2D, but this way I can simply render the original object’s textures directly, and items can be in piles up to hundreds of items, and this enables me to render them cheaply and efficiently with overlays rather than having 600 Sprite2Ds for 200 items with 3 overlays each), so I’m invested in this method.

How can I achieve this? In principle it seems pretty simple to me, but in practice Godot seems to make it arbitrarily difficult to draw anything using a material or shader other than the CanvasItem’s.

Solved: I ended up changing the wrapper class which was displaying the objects into a Node2D and creating a data structure to store commands. I converted my Draw() function into a DrawSequence() which returned a list of commands instead of calling the draw functions. Then, I added a Refresh() function which created a number of canvas items in the RenderingServer equal to my sequence’s length, and each time it’s refreshed it clears the canvas items and goes down the list to configure the canvas items to draw them with the appropriate texture and material (or null). This allows me to apply any arbitrary ShaderMaterial or any material I want to any layer by just not using Draw() at all and handling the canvas items directly in the RenderingServer. Here’s a video which explains the concept that I tweaked to suit my needs: https://www.youtube.com/watch?v=1PnG3r1rKcs