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
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)

View File

@ -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| {

View File

@ -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,

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 {
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: <glow::Context as HasContext>::Program,
pub vertex_array: <glow::Context as HasContext>::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));

View File

@ -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<G: Gl, T: TextureMap>(
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