diff --git a/imgui-glow-renderer/examples/01_basic.rs b/imgui-glow-renderer/examples/01_basic.rs index ed4803c..aea797e 100644 --- a/imgui-glow-renderer/examples/01_basic.rs +++ b/imgui-glow-renderer/examples/01_basic.rs @@ -20,6 +20,8 @@ fn main() { // OpenGL context from glow let gl = glow_context(&window); + // Outputting to screen, we want an sRGB framebuffer + unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) }; // OpenGL renderer from this crate let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context) diff --git a/imgui-glow-renderer/examples/03_triangle_gles.rs b/imgui-glow-renderer/examples/03_triangle_gles.rs index b895f56..32bcb7c 100644 --- a/imgui-glow-renderer/examples/03_triangle_gles.rs +++ b/imgui-glow-renderer/examples/03_triangle_gles.rs @@ -12,7 +12,7 @@ use utils::Triangler; fn main() { let (event_loop, window) = utils::create_window( - "Hello, triangle!", + "Hello, triangle! (GLES 3.0)", glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)), ); let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); @@ -23,7 +23,10 @@ fn main() { imgui_glow_renderer::Renderer::initialize(&gl, &mut imgui_context, &mut texture_map) .expect("failed to create renderer"); // Note the shader header now needs a precision specifier - let tri_renderer = Triangler::new(&gl, "#version 300 es\nprecision mediump float;"); + let tri_renderer = Triangler::new( + &gl, + "#version 300 es\nprecision mediump float;\n#define IS_GLES", + ); let mut last_frame = Instant::now(); event_loop.run(move |event, _, control_flow| { diff --git a/imgui-glow-renderer/examples/04_custom_textures.rs b/imgui-glow-renderer/examples/04_custom_textures.rs index e7913f3..1f98e6f 100644 --- a/imgui-glow-renderer/examples/04_custom_textures.rs +++ b/imgui-glow-renderer/examples/04_custom_textures.rs @@ -262,7 +262,7 @@ impl Lenna { gl.tex_image_2d( glow::TEXTURE_2D, 0, - glow::RGB as _, + glow::SRGB as _, width as _, height as _, 0, diff --git a/imgui-glow-renderer/examples/utils/mod.rs b/imgui-glow-renderer/examples/utils/mod.rs index e8bd995..c33bf53 100644 --- a/imgui-glow-renderer/examples/utils/mod.rs +++ b/imgui-glow-renderer/examples/utils/mod.rs @@ -23,7 +23,11 @@ pub fn create_window(title: &str, gl_request: GlRequest) -> (EventLoop<()>, Wind } pub fn glow_context(window: &Window) -> glow::Context { - unsafe { glow::Context::from_loader_function(|s| window.get_proc_address(s).cast()) } + unsafe { + let gl = glow::Context::from_loader_function(|s| window.get_proc_address(s).cast()); + gl.enable(glow::FRAMEBUFFER_SRGB); + gl + } } pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { @@ -49,6 +53,7 @@ pub fn imgui_init(window: &Window) -> (WinitPlatform, imgui::Context) { pub struct Triangler { pub program: ::Program, pub vertex_array: ::VertexArray, + is_gles: bool, } impl Triangler { @@ -61,18 +66,50 @@ const vec2 verts[3] = vec2[3]( ); out vec2 vert; +out vec4 color; + +vec4 srgb_to_linear(vec4 srgb_color) { + // Calcuation as documented by OpenGL + vec3 srgb = srgb_color.rgb; + vec3 selector = ceil(srgb - 0.04045); + vec3 less_than_branch = srgb / 12.92; + vec3 greater_than_branch = pow((srgb + 0.055) / 1.055, vec3(2.4)); + return vec4( + mix(less_than_branch, greater_than_branch, selector), + srgb_color.a + ); +} void main() { vert = verts[gl_VertexID]; + color = srgb_to_linear(vec4(vert, 0.5, 1.0)); gl_Position = vec4(vert - 0.5, 0.0, 1.0); } "#; const FRAGMENT_SHADER_SOURCE: &str = r#" in vec2 vert; -out vec4 colour; +in vec4 color; + +out vec4 frag_color; + +vec4 linear_to_srgb(vec4 linear_color) { +#ifdef IS_GLES + vec3 linear = linear_color.rgb; + vec3 selector = ceil(linear - 0.0031308); + vec3 less_than_branch = linear * 12.92; + vec3 greater_than_branch = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055; + return vec4( + mix(less_than_branch, greater_than_branch, selector), + linear_color.a + ); +#else + // For non-GLES, GL_FRAMEBUFFER_SRGB handles this for free + return linear_color; +#endif +} void main() { - colour = vec4(vert, 0.5, 1.0); + frag_color = linear_to_srgb(color); } "#; @@ -112,13 +149,21 @@ void main() { Self { program, vertex_array, + is_gles: imgui_glow_renderer::versions::GlVersion::read(gl).is_gles, } } } pub fn render(&self, gl: &glow::Context) { unsafe { - gl.clear_color(0.05, 0.05, 0.1, 1.0); + if self.is_gles { + // Specify clear color in sRGB space, since GL_FRAMEBUFFER_SRGB + // is not supported + gl.clear_color(0.05, 0.05, 0.1, 1.0); + } else { + // Specify clear color in linear space + gl.clear_color(0.004, 0.004, 0.01, 1.0); + } gl.clear(glow::COLOR_BUFFER_BIT); gl.use_program(Some(self.program)); gl.bind_vertex_array(Some(self.vertex_array)); diff --git a/imgui-glow-renderer/src/lib.rs b/imgui-glow-renderer/src/lib.rs index 82e3093..769e27e 100644 --- a/imgui-glow-renderer/src/lib.rs +++ b/imgui-glow-renderer/src/lib.rs @@ -793,9 +793,22 @@ uniform mat4 matrix; out vec2 fragment_uv; out vec4 fragment_color; +// Because imgui only specifies linear colors +vec4 srgb_to_linear(vec4 srgb_color) { + // Calcuation as documented by OpenGL + vec3 srgb = srgb_color.rgb; + vec3 selector = ceil(srgb - 0.04045); + vec3 less_than_branch = srgb / 12.92; + vec3 greater_than_branch = pow((srgb + 0.055) / 1.055, vec3(2.4)); + return vec4( + mix(less_than_branch, greater_than_branch, selector), + srgb_color.a + ); +} + void main() { fragment_uv = uv; - fragment_color = color; + fragment_color = srgb_to_linear(color); gl_Position = matrix * vec4(position.xy, 0, 1); } "#; @@ -806,8 +819,24 @@ in vec4 fragment_color; uniform sampler2D tex; layout (location = 0) out vec4 out_color; +vec4 linear_to_srgb(vec4 linear_color) { +#ifdef IS_GLES + vec3 linear = linear_color.rgb; + vec3 selector = ceil(linear - 0.0031308); + vec3 less_than_branch = linear * 12.92; + vec3 greater_than_branch = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055; + return vec4( + mix(less_than_branch, greater_than_branch, selector), + linear_color.a + ); +#else + // For non-GLES, GL_FRAMEBUFFER_SRGB handles this for free + return linear_color; +#endif +} + void main() { - out_color = fragment_color * texture(tex, fragment_uv.st); + out_color = linear_to_srgb(fragment_color * texture(tex, fragment_uv.st)); } "#; @@ -854,7 +883,7 @@ void main() { major = major, minor = minor * 10, es_extras = if is_gles { - " es\nprecision mediump float;" + " es\nprecision mediump float;\n#define IS_GLES" } else { "" }, @@ -968,14 +997,14 @@ fn prepare_font_atlas( gl.tex_image_2d( glow::TEXTURE_2D, 0, - glow::RGBA as _, + glow::SRGB8_ALPHA8 as _, atlas_texture.width as _, atlas_texture.height as _, 0, glow::RGBA, glow::UNSIGNED_BYTE, Some(atlas_texture.data), - ) + ); } fonts.tex_id = texture_map