I’m creating a custom shader for CanvasGroups. Basically, it’s a playing card with multiple elements inside it. I’m using the shader for multiple effects, including rounding the corners.
I got everything working properly when the card is in upright position (or rotated 90, 180, 270 degrees), but the UVs mess things up with any angle between those. UV is getting bigger when rotated, making its coordinates unreliable. Any ideas on how to fix this?
CanvasGroup has the shader and is the one being rotated. It doesn’t make a difference if I rotate a parent of the CanvasGroup either. The outcome is the same.
It’s a weird way of doing this. Why not contain everything in a control container like panel? With panel you get rounded cornered box for free, no shaders or textures needed.
But if you insist on doing it like this, simply leave the canvas group node be and put another sprite inside it that will draw the card background/frame
I’m still pretty new to Godot. If there’s a better way to achieve what I want, I’m absolutely open to new implementation ideas! Here’s what I want to be able to do:
Round corders (using an image mask or any other way of achieving it)
Build the card using multiple nodes (sprites and labels)
Be able to apply a singular shader to the whole card
I’m not sure how I could use a panel with rounded corners to achieve all 3 goals, especially the last one. That’s why I’m currently placing them into a CanvasGroup, to be able to apply a shader to the card as a single entity.
I’m not sure what you mean by this. By nature, CanvasGroup cannot have graphics applied to itself, but it works as a container for other nodes, like Sprite2Ds. I described by node tree in the opening post.
I fail to see how that changes anything. Can you post a list of your suggested node tree? I believe it won’t change the outcome of applying a shader to said CanvasGroup.
The shader in the canvas group doesn’t need to do the rounded corner part then. Just read the screen texture and assign to COLOR. It won’t have to deal with UV which gets extended to encompassing bounding box of all rotated children.
CanvasGroup
Background Sprite
Other stuff you currently have
That won’t accomplish what I show and explain, where I use the rounded corner image as a mask for everything inside the card. I can bake the rounded corners to any elements inside, but that doesn’t fix the UV issues. I want to have other shader effects on the card too, of which many use bitmaps to achieve said effects. They are stretched in a similar fashion whenever the card is rotated. The rounded corners is just the most visible one.
I would really want to get the UVs match the rotated CanvasGroup. That is the core issue I have.
Afaik, you can’t. CanvasGroup’s bounding box is not exposed to script so you have nothing to work with if you wanted to recalculate UVs. If the bounding box was known it could be possible with a bit of simple math to calculate the UV area that covers the content in rotated state.
If you need masking you can use canvas item’s clip children feature. Drop the canvas group and use a sprite as a root node, add an additional effect sprite on top of everything:
Clipping Sprite (rounded corner texture, enable clip children)
Your content
Effect sprite (shader drawing the effect)
That doesn’t let me apply a shader to your example’s root_mask (one of the main things I wanted to accomplish). Still, thank you so much for trying to help me with the problem.
Here’s the shader code we wrote to properly shrink UV for a rotated CanvasGroup in case someone else has the same problem.
Which the system doesn’t know and you need to guess/input it manually. If something is larger than the mask and sticks out (which is the sole purpose of using a mask) you’ll have hard time keeping track of that size.
Why do you need to run a shader on the canvas group? If you need to overlay a shader effect, just put an additional sprite on top of everything and run the shader on that sprite. Much simpler to manage than hacking the canvas group and guessing its bounding box size.
I’m setting it via code. I’m using this for my cards, which are always the same size. Scaling doesn’t affect this behaviour.
As far as I know, I cannot do ‘destructive’ shaders when I only overlay it to a new child node, like fading the card out using noise. I find it very helpful to be able to apply the shader directly to the group, not its children nodes. I’m not guessing the size of my CanvasGroup as it’s always the same size (and can be set in code too).
I’ve never worked with canvas groups but when working with shaders, having a single texture has many benefits. Consider using a viewporttexture on a sprite and add your shaders there. I’m working on a card game and most off the shelf shaders will work better in this setup.
I’m trying to avoid using ViewportTextures as they seem to be less performant compared to CanvasGroups (if you have dozens and dozens of cards on the screen at once). Though, they are pretty painful to work with shaders.
Going on a tangent here, and I understand exactly what you are thinking about, but have you considered maybe this falls under premature optimization? Please consider there are also significant overhead costs of maintaining extraneous nodes (specially when allocating and freeing). https://wiki.c2.com/?PrematureOptimization
While not new to me, this is extremely valuable guidance. I tried SubViewportViews before, and they weren’t performant enough with our number of cards. However, I might have messed up the testing and will try them again to see how they would perform.