Colorspace in glium

Date: 2023-07-31 | Author: Jason Eveleth

Table of Contents

I have been working on a small project to learn OpenGL. I decided to use Rust since it is the best programming language ever conceived by man. The obvious choice for an OpenGL project in Rust is to use glium. However, I found their API a little confusing. I thought it would be useful to have a guide on how to deal with colorspaces in glium and OpenGL. This is that guide. Note: This is my first foray into graphics, if anything is wrong, or hints at a misunderstanding on my part, please let me know.

Concepts involved

"help my colors are broken" flowchart (for the current glium way)

Many of the issues people have encountered would be solved by this flow chart, so I've included it before the rest of my learnings.

Summary of literature

Normally, color values in images are assumed to be in linear color space. If an image is in sRGB color format (ie. SrgbTexture2d vs Texture2d), values are assumed to be stored in sRGB.

"When fetching from sRGB images in shaders, values are converted from sRGB into linear colorspace." Thus, the shader only sees linear values.

If OpenGL needs to filter sampled values, the implementation is allowed to filter before or after the sRGB conversion (and since filtering is linear, you want it to happen in a linear space (ie. linear RGB)).

source

"Normally, sRGB images perform color correction, such that texture reads from them will always convert to linear [RGB]".

When writing from a fragment shader, we need to know how to interpret the values written by the shader. If the output buffer is linear RGB, we assume the answer is linear. But if we're writing to an sRGB image, maybe we want to write linear or sRGB. For this, we need a global toggle since the gamma correction partly depends on the output buffer. This is the GFS parameter.

When GFS is:

- sRGB: assume shader output is linear, so it converts linear RGB to sRGB - non-sRGB: no conversion

[1] you can check this with glGetFramebufferAttachmentParameter(GL_Framebuffer_SRGB)
Blending is a linear operation, so we need to convert to linear, then back to sRGB. If GFS is enabled, if a destination image is sRGB, the color will be converted to linear, blended w/ linear source and convert back. If GFS is disabled, "we assume the user knows what they are doing"; thus, "blending against an sRGB image will not perform any correction. This is usually not a good idea even if you are writing sRGB color values from the Fragment Shader."

source

This entire section is summarized by this graphic:

clear_color() vs clear_color_srgb()

If you call clear_color_srgb(), we enable GFS and record that in the global context if it wasn't already enabled. If you call clear_color(), we disable GFS (and record that in the global context) if it wasn't already.

source: (commit:filename:line-number) 5d50e70:src/ops/clear.rs:38

Github Issues

Issue #1793

Issue #1615

Issue #805

What should we do?

We can either hide the fact that OpenGL uses GFS, or we should be very transparent about what GFS means and how to interact with it.

Transparent Approach

Here, GFS should be enabled or disabled when the context is initialized and never changed (there is already support for this in ContextBuilder). Using clear_color() to change the GFS value seems like the wrong way to go since it introduces unnecessary complexity. I can't think of a use case where being able to change GFS at runtime is useful if we're trying to be transparent (feel free to point one out in the comments).

In this approach with GFS enabled, we still require users to use SrgbTextures and Textures, and to differentiate what type of values are stored there. But, there is no need to specify whether a program outputs sRGB, since we are going to use OpenGL's underlying implicit conversions.

In this approach, if you choose to disable GFS:

How to implement this approach

First, remove clear_color_srgb() and don't fiddle with GFS in clear_color(). Second, remove the code which enables GFS in use_program() at 5d50e70:src/program/program.rs:449.

Advantages of this approach

Disadvantages

Hidden approach

The user should never have to worry about GFS. This is most similar to how things are done now. They do still need to worry about whether their textures and programs contain/output sRGB values.

How to implement this approach

We shouldn't let users choose the GFS setting when initializing a context since they shouldn't worry about it. And GFS should always be enabled. We will still need users to tell us whether they're texture has linear or sRGB values, and whether their program outputs linear or sRGB. But, that is the only time they'll need to worry about colorspace, we'll handle the enabling/disabling of GFS behind the scenes (similar to how we do it in use_program() at 5d50e70:src/program/program.rs:449).

We also should remove clear_color_srgb() since users shouldn't worry about GFS.

Advantage of this approach

© Jason Eveleth 2023 · Powered by Franklin.jl · Last modified: December 31, 2024 Page Source