godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.17k stars 98 forks source link

Add a shader property to the StyleBox resource #3494

Open likeich opened 3 years ago

likeich commented 3 years ago

Describe the project you are working on

An Android launcher with complex UI and UI overlays.

Describe the problem or limitation you are having in your project

It is difficult and unintuitive to apply shaders to UI.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The main problem I face is that placing shaders on UI is difficult because it does not make use of Godot's theming system.

One of the strengths of Godot is how the theming system makes it easy to alter UI in many different places. If I add a shadow to a panel in my game's theme, all panels will apply that shadow as well automatically. This does not work with shaders, however.

I propose adding a shader property to the base StyleBox that will propagate the shader throughout the UI.

For example, if I add a blur shader to the panel StyleBox, then all panels created in my game will have that blur property. Currently, to do this in Godot I would have to find every panel I'm using manually and change it myself. If I eventually want to move to a different shader I would have to find all the panel nodes again to reapply the new shader.

Adding a shader property to StyleBoxes would enable many different UI effects and make it very easy to make polished and dynamic UI.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Add a shader property to the StyleBox resource.

If this enhancement will not be used often, can it be worked around with a few lines of script?

It would be used often and writing a script for this is not intuitive or simple.

Is there a reason why this should be core and not an add-on in the asset library?

It affects the core Godot UI.

Note: Discussed in #3485

fire-forge commented 3 years ago

I came across a use case for this today. I was adding a background blur shader to several UI nodes, and there was a Label that contained both its text and background StyleBox. When I added the blur shader to the Label node, it applied the shader to both the text and the StyleBox background it was on. To prevent this, I had to add a PanelContainer with the Label's StyleBox, remove the StyleBox from the Label, and put the shader on the PanelContainer so that it didn't affect the text's appearance. This workaround was fairly simple, but having a dedicated property for shaders on StyleBox would improve the UI creation experience.

Also, what would happen if both a StyleBox and the node it was on had a shader? Would it use both shaders, or would the node's shader overwrite the StyleBox's shader?

YuriSizov commented 3 years ago

@fire540 We've looked a bit into the idea before and it seems that the current limitation may be with how shaders are applied (per object, impossible to add to teach stylebox individually). But for this feature to be useful, it would need to work in multiple steps: Stylebox's shader would be applied to the stylebox itself, while the node shader would then be applied to the node as a whole.

sosasees commented 2 years ago

I have yet another use for a shader inside of Themes: I want to have nice rounded corners on my panels (and other Controls), but not just any old rounded corners but Squircled corners (Squircled corners look much better because you can't see exactly where the "circle" part ends and the "rectangle" part begins, while with a circle you Can).

While i could easily add a squircle image, i really like to use Distance Field shaders for resolution-independent graphics, and this Squircle shader is an example of exactly such a distance field shader.

This is my minimal reprodution project of Squircled Panels in Godot v3.4.3.stable.official [242c05d12]: squircled-panels.zip

98teg commented 1 year ago

Here are some things that I can think of that are possible right now, but that would be much more simple if we could use a shader with each StyleBox:

Here are the things that would only be possible if we use a shader inside a StyleBox:

And here is a thing that could be possible as a future enhancement... transition animations. Let's say that every StyleBox shader would receive a uniform called START_TIME. This uniform contains the value of the global built-in TIME when this StyleBox was first rendered. Then, by using both START_TIME and TIME it would be possible to create a transition animation. This would be a different proposal of course, but it is something that this proposal would enable.

Note: When I talk about whether it is possible, I'm referring to creating these effects just using the theme itself. All of this is possible by creating custom nodes or by other means.

98teg commented 1 year ago

@YuriSizov Making the StyleBox itself a node may serve as a workaround. However, this change would probably require a lot of refactoring.

YuriSizov commented 1 year ago

@98teg That's not a fix, that's a workaround. You can achieve it already by creating Panels or just custom controls that only draw single stylebox, and add shaders to them.

sosasees commented 1 year ago

@98teg, the stylebox should be a variable — like it is now. it should not be a node.

making the panel's stylebox a node would be like making the player's run speed or the label's text a node: the tree would get really messy really fast.

98teg commented 1 year ago

@YuriSizov @Sosasees I'm talking about a node that, let's say, a button would instantiate automatically. The button would be responsible for both, maintaining this node StyleBox and its z-index to render below its content. Much like a file dialog instantiate buttons. This would be a node invisible to the user, sorry if I expressed myself incorrectly.

YuriSizov commented 1 year ago

@98teg Yes, I understand the idea, but it's still a very hacky workaround. It's okay for a "fix" on the project side, but it's not a proper solution for the engine which we fully control. Resources and Nodes serve different purposes, and also nodes are never truly invisible, because they affect the whole tree structure. You may not see the internals of, say, ScrollContainer in the editor, but you still see them in during the runtime. So the solution here is still to somehow associate StyleBoxes with some RIDs to work with the rendering server.

sosasees commented 1 year ago

yes, @YuriSizov. if we want to fix this in the godot editor, it must be a real fix.

we can already do the hacky fix with the tools that the godot editor gives us now:

in a scene like this

an easy hack is

  1. set the alpha channel of the _selfmodulate color of the panelcontainer to 0. now the background is gone, but the margins don't change and the content is still visible
  2. as a child of the panelcontainer, add a panel — it should have the same stylebox as the now-invisible panelcontainer, so the margins are the same. now you got the panel background back
  3. now you can add a shader to the child panel. this changes the background without breaking the content
Kade-github commented 1 year ago

Found another use for this, I was attempting to change the StyleFlatBox of the TabContainer's buttons to make it use a shader, but I was unable.

It would be a great feature to add.

MartinHaeusler commented 10 months ago

I want to apply background blur to my panels by default. While this is possible by assigning a shader to each panel that needs it, it would be cool to:

neruthes commented 9 months ago

This proposal looks good to me. I want to add a breathing effect to buttons so any pressed button will be breathing (i.e. COLOR.a changes according to cos(TIME)). These buttons are in the same theme so I think adding a ShaderMaterial to the specific StyleBoxFlat for pressed state of Label in the theme would be of great convenience.

In my specific case, it is safe to override existing material on the button when the active StyleBox offers another ShaderMaterial. It may be a good idea to add a new flag prefer_stylebox_material on CanvasItem.

I can think of an workaround though. I may create my own BreathingButton class which loads/unloads the ShaderMaterial according to the toggled signal. But this way may harm the portability of some code. Can someone recommend a better solution? Thanks.

Gastronok commented 5 months ago

I'm terrible at creating UI graphics. Fortunately there are many UI packs freely available on the internet. Almost all of them come preconfigured with some specific color(s) in mind.

Modulate is not always appropriate for changing colors; for variations on a theme HSL adjustments would be more appropriate. The common response when asking for HSL modulation is to use a shader -- https://github.com/godotengine/godot-proposals/issues/96

But right now themes don't include shaders, so I can't adjust the hue of the texture on my button styles. At least not if I want to use Godot's Theme engine.

(As an aside I'd be fine if HSL adjustments became first-class options, but this proposal seems more general / flexible.)

Calinou commented 5 months ago

But right now themes don't include shaders, so I can't adjust the hue of the texture on my button styles. At least not if I want to use Godot's Theme engine.

You could automate a hue adjustment on all images included within the UI pack using an ImageMagick command or similar.

Gastronok commented 5 months ago

Consider the use case of different variations on a single image for button highlight / disabled / hover colors. Using an external library would require creating a separate image per color variant.

If slightly recoloring existing images weren't a common workflow, why would modulate be included so commonly amongst theme configuration options? It's just that the multiplicative color adjustments don't work as well on sprites that weren't explicitly designed for them, and some of us are not good at designing sprites.

TheDuriel commented 4 months ago

I've ended up creating an equivalent shittier version of this in pure GDScript that works within the constraints of how Theming actually works. Per node, not per stylebox.

https://github.com/TheDuriel/DurielUtilities