Handle sRGB colours properly

This commit is contained in:
John-Mark Allen 2021-07-04 22:58:02 +01:00 committed by Jack Spira
parent 0cafc5c22d
commit 84020a6bc0
5 changed files with 91 additions and 12 deletions

View File

@ -20,6 +20,8 @@ fn main() {
// OpenGL context from glow // OpenGL context from glow
let gl = glow_context(&window); let gl = glow_context(&window);
// Outputting to screen, we want an sRGB framebuffer
unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
// OpenGL renderer from this crate // OpenGL renderer from this crate
let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context) let mut ig_renderer = imgui_glow_renderer::AutoRenderer::initialize(gl, &mut imgui_context)

View File

@ -12,7 +12,7 @@ use utils::Triangler;
fn main() { fn main() {
let (event_loop, window) = utils::create_window( let (event_loop, window) = utils::create_window(
"Hello, triangle!", "Hello, triangle! (GLES 3.0)",
glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)), glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)),
); );
let (mut winit_platform, mut imgui_context) = utils::imgui_init(&window); 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) imgui_glow_renderer::Renderer::initialize(&gl, &mut imgui_context, &mut texture_map)
.expect("failed to create renderer"); .expect("failed to create renderer");
// Note the shader header now needs a precision specifier // 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(); let mut last_frame = Instant::now();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {

View File

@ -262,7 +262,7 @@ impl Lenna {
gl.tex_image_2d( gl.tex_image_2d(
glow::TEXTURE_2D, glow::TEXTURE_2D,
0, 0,
glow::RGB as _, glow::SRGB as _,
width as _, width as _,
height as _, height as _,
0, 0,

View File

@ -23,7 +23,11 @@ pub fn create_window(title: &str, gl_request: GlRequest) -> (EventLoop<()>, Wind
} }
pub fn glow_context(window: &Window) -> glow::Context { 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) { 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 struct Triangler {
pub program: <glow::Context as HasContext>::Program, pub program: <glow::Context as HasContext>::Program,
pub vertex_array: <glow::Context as HasContext>::VertexArray, pub vertex_array: <glow::Context as HasContext>::VertexArray,
is_gles: bool,
} }
impl Triangler { impl Triangler {
@ -61,18 +66,50 @@ const vec2 verts[3] = vec2[3](
); );
out vec2 vert; 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() { void main() {
vert = verts[gl_VertexID]; vert = verts[gl_VertexID];
color = srgb_to_linear(vec4(vert, 0.5, 1.0));
gl_Position = vec4(vert - 0.5, 0.0, 1.0); gl_Position = vec4(vert - 0.5, 0.0, 1.0);
} }
"#; "#;
const FRAGMENT_SHADER_SOURCE: &str = r#" const FRAGMENT_SHADER_SOURCE: &str = r#"
in vec2 vert; 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() { void main() {
colour = vec4(vert, 0.5, 1.0); frag_color = linear_to_srgb(color);
} }
"#; "#;
@ -112,13 +149,21 @@ void main() {
Self { Self {
program, program,
vertex_array, vertex_array,
is_gles: imgui_glow_renderer::versions::GlVersion::read(gl).is_gles,
} }
} }
} }
pub fn render(&self, gl: &glow::Context) { pub fn render(&self, gl: &glow::Context) {
unsafe { 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.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program)); gl.use_program(Some(self.program));
gl.bind_vertex_array(Some(self.vertex_array)); gl.bind_vertex_array(Some(self.vertex_array));

View File

@ -793,9 +793,22 @@ uniform mat4 matrix;
out vec2 fragment_uv; out vec2 fragment_uv;
out vec4 fragment_color; 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() { void main() {
fragment_uv = uv; fragment_uv = uv;
fragment_color = color; fragment_color = srgb_to_linear(color);
gl_Position = matrix * vec4(position.xy, 0, 1); gl_Position = matrix * vec4(position.xy, 0, 1);
} }
"#; "#;
@ -806,8 +819,24 @@ in vec4 fragment_color;
uniform sampler2D tex; uniform sampler2D tex;
layout (location = 0) out vec4 out_color; 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() { 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, major = major,
minor = minor * 10, minor = minor * 10,
es_extras = if is_gles { es_extras = if is_gles {
" es\nprecision mediump float;" " es\nprecision mediump float;\n#define IS_GLES"
} else { } else {
"" ""
}, },
@ -968,14 +997,14 @@ fn prepare_font_atlas<G: Gl, T: TextureMap>(
gl.tex_image_2d( gl.tex_image_2d(
glow::TEXTURE_2D, glow::TEXTURE_2D,
0, 0,
glow::RGBA as _, glow::SRGB8_ALPHA8 as _,
atlas_texture.width as _, atlas_texture.width as _,
atlas_texture.height as _, atlas_texture.height as _,
0, 0,
glow::RGBA, glow::RGBA,
glow::UNSIGNED_BYTE, glow::UNSIGNED_BYTE,
Some(atlas_texture.data), Some(atlas_texture.data),
) );
} }
fonts.tex_id = texture_map fonts.tex_id = texture_map