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.
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()));
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);