Open coderedart opened 2 years ago
Blur is difficult, as it requires a post-processing pass which complicates the egui rendering by a lot.
But adding custom Shape
:s as background for widgets would be nice.
Once https://github.com/emilk/egui/issues/1516 is solved this could be as easy as ui.label(…).background(my_texture)
I'm not sure this is globally portable. This would require a compositor. For X11, at least, it's not guaranteed that a compositor is running at all, meaning that you would have to implement composition from scratch.
I'm not sure this is globally portable. This would require a compositor. For X11, at least, it's not guaranteed that a compositor is running at all, meaning that you would have to implement composition from scratch.
Compositor is required for window level transparency. I am talking within egui itself.
Basically, a container just draws a rectangle and fills it with background color atm. But, by allowing containers to instead take a paint callback which will draw the rectangle and use fragment shader to mix frame buffer and maybe background color of style to create blur effect.
Egui itself has no idea about what is happening. It just takes a shape paint callback when container needs to draw its background. The callback could simply be drawing a texture or blur effect or whatever as the background of the window.
I was able to accomplish this - see here.
For anyone else who wants to accomplish it, the idea is basically this: register two textures at window size, let's call them back
and front
. The main render pass should render to the front
texture view, and you should do one final render pass which copies front
to the main surface view. You'll also have to recreate these textures on re-size so they are synced to the current window size.
Note that if you use eframe
(or the winit
extensions for egui-wgpu
/egui-glow
) then this will not be possible, since those frameworks render directly onto the window surface.
You'll need a basic blur shader which takes a texture as input and performs a gaussian blur (you can optimize this in a few ways, primarily as a Separable Filter and/or by performing many simple box blurs). I'll call this shader blur_rect
.
Basically, you go:
front
-- blur_rect
--> back
back
-- (copy) --> front
Pass 1 should perform a clear operation on back
to transparent.
The reason you need two render passes and textures here is because you generally can't write to a texture while also reading from it. If anyone sees any way to do this with just one texture/pass, I'd love to hear it!
For me, I used WGPU. Since a wgpu::RenderPass
encapsulates it's wgpu::Encoder
, it would normally not be possible to create another render pass in a paint callback, so I had to write the following hack (requires package wgpu-core
):
I thought a lot about how you could possibly pass the encoder to a callback, but I literally don't see any way since it's being mutably borrowed by the render_pass. I'm assuming that egui-glow
exposes something like a native gl
context, so it probably doesn't have the above issue.
Let me know if that makes sense @coderedart. I can draft a simple example repo in OpenGL (or WGPU) if you're interested. I'm also working on some more in-depth tutorials for egui where I'll probably go over it in a lot more in depth (as well as other stuff like popout-window context menus) :)
Regarding implementing this into egui itself, the existence of blur requires rendering to a separate texture in some capacity (the window surface texture [known in opengl as "default framebuffer"] cannot be read from, only written to as a color attachment). This is additional overhead that IMO users shouldn't be forced to opt into. These issues also prevents abstracting away blur-regions into something like a library, since it requires a totally different render pipeline.
One last aside: one major optimization is to perform filter separation on the blur_rect
shader between the two render passes. Basically, you can perform the blur purely horizontally on the first pass from front
to back
, and then purely vertically back
to front
. Since the filter is separable, this will work, and it takes the algorithm from $O(r^2)$ to $O(r)$, where $r$ is the blur radius.
Is your feature request related to a problem? Please describe. Colors of a widget decided by the
Style
. sometimes, we require more customization to create great looking games / apps.Describe the solution you'd like allow widgets / areas to decide what they want to draw as their background. I would say the present custom paint callbacks should be a good way to let users do whatever they want.
Describe alternatives you've considered the obvious simpler alternative is to use a image (
TextureID
) as a background. but sometimes we need to use the already drawn background pixels (game scenes / other widgets) to derive the present widget's background pixels. for example, blur themes popular in KDE desktop ricing. basically the background of a widget is transparent BUT the stuff behind the widget is slightly blurred.https://reddit.com/r/unixporn should have more examples, but a simple screenshot is here to clearly show what i mean by blur
Additional context if using paint callbacks seems too risky / unsafe. just using a texture as a background of widget and being able to set transparency / blur in egui Style is fine too. once i realized how cool blur Styles in egui would look, i just can't stop imagining all the themes people would share.