Search code examples
openglrustegui

Why is my OpenGL Texutre render darkened? (The RGB values are squared)


I've been using glow to do some opengl rendering in rust. It's going well, but rendering to a texture vs. rendering to an image gives me different results. I know a naive way to solve it, I just don't understand it.

enter image description here

The image on the right is rendering right to the screen, while the lower left is created by rendering the same mesh to a texture of the same size using the same draw function (passing the pixel data to egui's ColorImage). As you can see, it's too dark, and this is also true when I save the pixel data to a file (using rust's Image crate).

But, if a channel's value is full in the render, it's also full in the output, almost like the RGB values are squared in a range of 0-1.0 before being converted to RGB8. Sure enough, I tried undoing that in the pixel data (flipped_buffer[i1] = ((buffer[i2] as f32 / 255.).sqrt() * 255.).round() as u8;), and it started looking correct!

So WHY? It's such a specific thing, and I can even imagine it being a useful way of mapping colors (since eyes distinguish darker values better than brighter ones), but why did it happen here?

The code for writing to a texture is based on the tutorial here, but using glow in rust instead of C/C++, and using RGBA instead of RGB.

Texture Creation Exerpt:

let gl_texture = self.gl.create_texture()?;
self.gl.bind_texture(glow::TEXTURE_2D, Some(gl_texture));
self.gl.tex_image_2d(glow::TEXTURE_2D, 0, glow::RGBA as i32, width as i32, height as i32, 0, glow::RGBA, glow::UNSIGNED_BYTE, None);
self.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32);
self.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32);

Pixel Data Read Exerpt:

let mut buffer = vec![0 as u8; (width * height * 4) as usize];
self.gl.get_tex_image(
    glow::TEXTURE_2D,
    0,
    glow::RGBA,
    glow::UNSIGNED_BYTE,
    glow::PixelPackData::Slice(buffer.as_mut_slice()));

Solution

  • You seem to have GL_FRAMEBUFFER_SRGB enabled somewhere. The effect of this is that when you render to the screen the linear values coming from the shader are gamma-compressed with the sRGB transfer function (which isn't exactly a square root, but you got the idea). When you render to a non-sRGB texture, the conversion isn't happening at all, because almost all 8-bit file formats and APIs assume that the 8-bit data is already in sRGB color-space. Consequently, when you save it to a file and open it with a viewer that expects it to be sRGB, it looks darker. That "egui" library seems to expect sRGB images too.

    The solution is to change your internal texture format to GL_SRGB8_ALPHA8:

    self.gl.tex_image_2d(glow::TEXTURE_2D, 0, glow::SRGB8_ALPHA8 as i32, width as i32, height as i32, 0, glow::RGBA, glow::UNSIGNED_BYTE, None);