Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions crates/egui-wgpu/src/egui.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ fn vs_main(

@fragment
fn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
// We always have an sRGB aware texture at the moment.
let tex_linear = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
let tex_gamma = gamma_from_linear_rgba(tex_linear);
// We use sRGB unware textures, and store (premultiplied) sRGB values.
// Hence any interpolation done by the GPU will be in gamma space.
// Note: Because we store premultiplied alpha, we can't rely on sRGB aware
// textures that convert "sRGB -> linear RGB" before interpolating.
let tex_gamma = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
var out_color_gamma = in.color * tex_gamma;
// Dither the float color down to eight bits to reduce banding.
// This step is optional for egui backends.
Expand All @@ -115,9 +117,11 @@ fn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {

@fragment
fn fs_main_gamma_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
// We always have an sRGB aware texture at the moment.
let tex_linear = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
let tex_gamma = gamma_from_linear_rgba(tex_linear);
// We use sRGB unware textures, and store (premultiplied) sRGB values.
// Hence any interpolation done by the GPU will be in gamma space.
// Note: Because we store premultiplied alpha, we can't rely on sRGB aware
// textures that convert "sRGB -> linear RGB" before interpolating.
let tex_gamma = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
var out_color_gamma = in.color * tex_gamma;
// Dither the float color down to eight bits to reduce banding.
// This step is optional for egui backends.
Expand Down
4 changes: 2 additions & 2 deletions crates/egui-wgpu/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,9 +629,9 @@ impl Renderer {
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, // Minspec for wgpu WebGL emulation is WebGL2, so this should always be supported.
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
})
};
let origin = wgpu::Origin3d::ZERO;
Expand Down
9 changes: 4 additions & 5 deletions crates/egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,10 @@
//!
//! * 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`).
//! * egui prefers linear color spaces for all blending so:
//! * 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`).
//! * egui prefers gamma color spaces for all blending so:
//! * Do NOT use an sRGBA-aware texture (NOT `GL_SRGB8_ALPHA8`).
//! * Multiply texture and vertex colors in gamma space
//! * HOWEVER: Turn on sRGBA/gamma framebuffer if available (`GL_FRAMEBUFFER_SRGB`).
//! * Otherwise: gamma-encode the colors before you write them again.
//!
//!
Expand Down
16 changes: 9 additions & 7 deletions crates/egui_demo_lib/src/rendering_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl ColorTest {
ui.separator();

// TODO(emilk): test color multiplication (image tint),
// to make sure vertex and texture color multiplication is done in linear space.
// to make sure vertex and texture color multiplication is done in the correct space.

ui.label("Gamma interpolation:");
self.show_gradients(ui, WHITE, (RED, GREEN), Interpolation::Gamma);
Expand Down Expand Up @@ -191,8 +191,8 @@ impl ColorTest {

ui.separator();

ui.label("Linear interpolation (texture sampling):");
self.show_gradients(ui, WHITE, (RED, GREEN), Interpolation::Linear);
ui.label("Texture interpolation (texture sampling) should be in gamma space:");
self.show_gradients(ui, WHITE, (RED, GREEN), Interpolation::Gamma);
}

fn show_gradients(
Expand Down Expand Up @@ -245,11 +245,10 @@ impl ColorTest {
let g = Gradient::endpoints(left, right);

match interpolation {
Interpolation::Linear => {
// texture sampler is sRGBA aware, and should therefore be linear
self.tex_gradient(ui, "Texture of width 2 (test texture sampler)", bg_fill, &g);
}
Interpolation::Linear => {}
Interpolation::Gamma => {
self.tex_gradient(ui, "Texture of width 2 (test texture sampler)", bg_fill, &g);

// vertex shader uses gamma
self.vertex_gradient(
ui,
Expand Down Expand Up @@ -330,7 +329,10 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Respon

#[derive(Clone, Copy)]
enum Interpolation {
/// egui used to want Linear interpolation for some things, but now we're always in gamma space.
#[expect(unused)]
Linear,

Gamma,
}

Expand Down
10 changes: 2 additions & 8 deletions crates/egui_glow/src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,7 @@ impl Painter {

let supported_extensions = gl.supported_extensions();
log::trace!("OpenGL extensions: {supported_extensions:?}");
let srgb_textures = shader_version == ShaderVersion::Es300 // WebGL2 always support sRGB
|| supported_extensions.iter().any(|extension| {
// EXT_sRGB, GL_ARB_framebuffer_sRGB, GL_EXT_sRGB, GL_EXT_texture_sRGB_decode, …
extension.contains("sRGB")
});
log::debug!("SRGB texture Support: {:?}", srgb_textures);
let srgb_textures = false; // egui wants linear textures

let supports_srgb_framebuffer = !cfg!(target_arch = "wasm32")
&& supported_extensions.iter().any(|extension| {
Expand All @@ -202,11 +197,10 @@ impl Painter {
&gl,
glow::FRAGMENT_SHADER,
&format!(
"{}\n#define NEW_SHADER_INTERFACE {}\n#define DITHERING {}\n#define SRGB_TEXTURES {}\n{}\n{}",
"{}\n#define NEW_SHADER_INTERFACE {}\n#define DITHERING {}\n{}\n{}",
shader_version_declaration,
shader_version.is_new_shader_interface() as i32,
dithering as i32,
srgb_textures as i32,
shader_prefix,
FRAG_SRC
),
Expand Down
17 changes: 0 additions & 17 deletions crates/egui_glow/src/shader/fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,8 @@ vec3 dither_interleaved(vec3 rgb, float levels) {
return rgb + noise / (levels - 1.0);
}

// 0-1 sRGB gamma from 0-1 linear
vec3 srgb_gamma_from_linear(vec3 rgb) {
bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
vec3 lower = rgb * vec3(12.92);
vec3 higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055);
return mix(higher, lower, vec3(cutoff));
}

// 0-1 sRGBA gamma from 0-1 linear
vec4 srgba_gamma_from_linear(vec4 rgba) {
return vec4(srgb_gamma_from_linear(rgba.rgb), rgba.a);
}

void main() {
#if SRGB_TEXTURES
vec4 texture_in_gamma = srgba_gamma_from_linear(texture2D(u_sampler, v_tc));
#else
vec4 texture_in_gamma = texture2D(u_sampler, v_tc);
#endif

// We multiply the colors in gamma space, because that's the only way to get text to look right.
vec4 frag_color_gamma = v_rgba_in_gamma * texture_in_gamma;
Expand Down
Loading