Description
Since most (all?) widgets end up calling DrawImage
in the end, it should be possible to pass a user-defined shader in there.
This is how I do it in my other library:
https://github.com/quasilyte/ebitengine-graphics/blob/60f2c18c54bf18fef75568750d94d8a97bb6408f/shader.go#L13-L28
Every object that supports shaders has a *Shader
property. A nil shader means no shader drawing is needed. A non-nil shader means we might n
5E95
eed to use the shader (unless it's disabled, but that feature can be skipped).
It's important to have a wrapper around *ebiten.Shader
as we often need extra source textures for running shaders (for noise, entropy, etc.).
Here is an example of how Sprite handles both cases with and without shaders:
https://github.com/quasilyte/ebitengine-graphics/blob/60f2c18c54bf18fef75568750d94d8a97bb6408f/sprite.go#L361
Perhaps this pattern can be refactored in a way that would allow abstracting this drawing process in a simple func like
func draw(options DrawOptions) {}
Where we would either have options.Shader
or not, and this function would do everything we need.
This will add a slight overhead of +1 pointer per widget that supports shaders and a bit of conditional logic for drawing, but overall it should be negligible as we can always do a fast-path with w.Shader == nil
.
Here is an incomplete list of things shaders can enable:
- custom press animations (a poor man's solution, but it might work!)
- pretty idle animations for buttons
- button texturing (e.g. patterns and other filters)
- texture repetition (could make Add a repeated image in addition to the nineslice image? #134 less important)
- they can replace the need for colorscaling options (as you can modify colors in the shader directly)
Imagine a button being dissolved in-out, etc.
Potential problems:
- what if ebitenui wants to use a shader of its own? Ebitengine only supports 1 shader per draw, so it might become ineffective to chain more than 1 shader
- how will it interact with non-trivial features like masks (e.g. scroll containers)? Will it operate on a visible part of a subimage
- should there be per-state shaders, e.g. a shader per every button state? Or should there be a library-controlled uniform variable that is added for every shader which could make the shader understand which exact state the widget is in?
In any case, this feature should be marked as experimental for a while so we can try it out and improve its API and implementation details before committing to a specific decision.