diff --git a/README.md b/README.md index 66278f74..19b9b223 100644 --- a/README.md +++ b/README.md @@ -183,9 +183,13 @@ loop { } ``` -For a reference OpenGL backend, [see the `egui_glium` painter](https://github.com/emilk/egui/blob/master/egui_glium/src/painter.rs). +For a reference OpenGL backend, see [the `egui_glium` painter](https://github.com/emilk/egui/blob/master/egui_glium/src/painter.rs) or [the `egui_web` `WebGL` painter](https://github.com/emilk/egui/blob/master/egui_web/src/webgl1.rs). -#### Debugging your integration +### Debugging your integration + +#### Things look jagged + +* Turn off backface culling. #### My text is blurry @@ -194,9 +198,13 @@ For a reference OpenGL backend, [see the `egui_glium` painter](https://github.co #### My windows are too transparent or too dark -* Make sure your texture sampler is clamped. -* Make sure you consider sRGB (gamma) in your shaders. -* Egui uses premultiplied alpha, so make sure your blending function is `(ONE, ONE_MINUS_SRC_ALPHA)` +* Egui uses premultiplied alpha, so make sure your blending function is `(ONE, ONE_MINUS_SRC_ALPHA)`. +* Make sure your texture sampler is clamped (`GL_CLAMP_TO_EDGE`). +* Use an sRGBA-aware texture if available (e.g. `GL_SRGB8_ALPHA8`). + * Otherwise: remember to decode gamma in the fragment shader. +* Decode the gamma of the incoming vertex colors in your vertex shader. +* Turn on sRGBA/linear framebuffer if available (`GL_FRAMEBUFFER_SRGB`). + * Otherwise: gamma-encode the colors before you write them again. ## Other diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index fa960c15..97770c47 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -43,6 +43,7 @@ const VERTEX_SHADER_SOURCE: &str = r#" 1.0 - 2.0 * a_pos.y / u_screen_size.y, 0.0, 1.0); + // Egui encodes vertex colors in gamma spaces, so we must decode the colors here: v_rgba = linear_from_srgba(a_srgba); v_tc = a_tc; } @@ -226,6 +227,9 @@ impl Painter { ..Default::default() }; + // Egui outputs triangles in both winding orders: + let backface_culling = glium::BackfaceCullingMode::CullingDisabled; + // Transform clip rect to physical pixels: let clip_min_x = pixels_per_point * clip_rect.min.x; let clip_min_y = pixels_per_point * clip_rect.min.y; @@ -245,6 +249,7 @@ impl Painter { let params = glium::DrawParameters { blend, + backface_culling, scissor: Some(glium::Rect { left: clip_min_x, bottom: height_in_pixels - clip_max_y, diff --git a/egui_web/src/webgl1.rs b/egui_web/src/webgl1.rs index fadfd9ff..feddaa84 100644 --- a/egui_web/src/webgl1.rs +++ b/egui_web/src/webgl1.rs @@ -39,6 +39,7 @@ const VERTEX_SHADER_SOURCE: &str = r#" 1.0 - 2.0 * a_pos.y / u_screen_size.y, 0.0, 1.0); + // Egui encodes vertex colors in gamma spaces, so we must decode the colors here: v_rgba = linear_from_srgba(a_srgba); v_tc = a_tc; } @@ -75,7 +76,13 @@ const FRAGMENT_SHADER_SOURCE: &str = r#" } void main() { + // We must decode the colors, since WebGL doesn't come with sRGBA textures: vec4 texture_rgba = linear_from_srgba(texture2D(u_sampler, v_tc) * 255.0); + + /// Multiply vertex color with texture color (in linear space). + gl_FragColor = v_rgba * texture_rgba; + + // We must gamma-encode again since WebGL doesn't support linear blending in the framebuffer. gl_FragColor = srgba_from_linear(v_rgba * texture_rgba) / 255.0; // WebGL doesn't support linear blending in the framebuffer, @@ -456,12 +463,11 @@ impl crate::Painter for WebGlPainter { let gl = &self.gl; gl.disable(Gl::SCISSOR_TEST); - gl.viewport( - 0, - 0, - self.canvas.width() as i32, - self.canvas.height() as i32, - ); + + let width = self.canvas.width() as i32; + let height = self.canvas.height() as i32; + gl.viewport(0, 0, width, height); + let clear_color: Color32 = clear_color.into(); gl.clear_color( clear_color[0] as f32 / 255.0, diff --git a/egui_web/src/webgl2.rs b/egui_web/src/webgl2.rs index bd397603..96a74614 100644 --- a/egui_web/src/webgl2.rs +++ b/egui_web/src/webgl2.rs @@ -41,6 +41,7 @@ const VERTEX_SHADER_SOURCE: &str = r#" 1.0 - 2.0 * a_pos.y / u_screen_size.y, 0.0, 1.0); + // Egui encodes vertex colors in gamma spaces, so we must decode the colors here: v_rgba = linear_from_srgba(a_srgba); v_tc = a_tc; } @@ -65,8 +66,13 @@ const FRAGMENT_SHADER_SOURCE: &str = r#" } void main() { + // The texture is set up with `SRGB8_ALPHA8`, so no need to decode here! vec4 texture_rgba = texture2D(u_sampler, v_tc); - // gl_FragColor = v_rgba * texture_rgba; + + /// Multiply vertex color with texture color (in linear space). + gl_FragColor = v_rgba * texture_rgba; + + // We must gamma-encode again since WebGL doesn't support linear blending in the framebuffer. gl_FragColor = srgba_from_linear(v_rgba * texture_rgba) / 255.0; // WebGL doesn't support linear blending in the framebuffer, @@ -420,8 +426,6 @@ impl crate::Painter for WebGl2Painter { let gl = &self.gl; gl.bind_texture(Gl::TEXTURE_2D, Some(&self.egui_texture)); - // TODO: https://developer.mozilla.org/en-US/docs/Web/API/EXT_sRGB - // https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ let level = 0; let internal_format = Gl::SRGB8_ALPHA8; let border = 0; @@ -448,12 +452,11 @@ impl crate::Painter for WebGl2Painter { let gl = &self.gl; gl.disable(Gl::SCISSOR_TEST); - gl.viewport( - 0, - 0, - self.canvas.width() as i32, - self.canvas.height() as i32, - ); + + let width = self.canvas.width() as i32; + let height = self.canvas.height() as i32; + gl.viewport(0, 0, width, height); + let clear_color: Color32 = clear_color.into(); gl.clear_color( clear_color[0] as f32 / 255.0, diff --git a/epaint/src/triangles.rs b/epaint/src/triangles.rs index 8db0ec1b..010cf342 100644 --- a/epaint/src/triangles.rs +++ b/epaint/src/triangles.rs @@ -24,6 +24,8 @@ pub struct Vertex { #[derive(Clone, Debug, Default)] pub struct Triangles { /// Draw as triangles (i.e. the length is always multiple of three). + /// + /// Egui is NOT consistent with what winding order it uses, so turn off backface culling. pub indices: Vec, /// The vertex data indexed by `indices`.