wgpu/documentation/color/srgb.rs
1/*!
2# Texture Color Formats and sRGB Conversions
3
4Tips and common misconceptions about color formats. For surface output,
5wide-gamut, and high-dynamic-range color, see
6[HDR and Wide-Gamut Color Spaces](crate::documentation::color::hdr_surfaces).
7
8_(The following is adapted from conversations on the `wgpu` users Matrix
9channel, saved here as a useful resource for future `wgpu` developers.)_
10
11Digital color can be an extremely confusing topic, and the resources in
12common circulation aren't always great. Modern low-level graphics APIs (such
13as those under the hood of `wgpu`) give us powerful tools to manipulate color
14data, though this comes at the cost of having to understand several low-level
15principles in how digital color works.
16
17The best place to start is to have an understanding of the problems and
18history of gamma correction. This page won't go deeply into the topic, but
19some useful links can be found here:
20
21- [What every coder should know about gamma](https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/)
22- [Hitchhiker's Guide to Digital Color](https://hg2dc.com/)
23- [Learn OpenGL — Gamma Correction](https://learnopengl.com/Advanced-Lighting/Gamma-Correction)
24
25## What do `TextureFormat`s do?
26
27There are many different texture formats you can specify for the format of
28your color data. This can be image data you upload from the CPU, shaded
29geometry rendered to intermediate textures (what we used to call frame
30buffers in OpenGL), compute shader targets, or geometry shaded to the window
31surface itself.
32
33We won't go into all the different formats here, but it's useful to talk
34about two of the most commonly used formats and some misconceptions about
35what they do (and don't) do.
36
37## What are Unorm and UnormSrgb?
38
39Some of the most common formats you will see used for texture data, render
40targets, etc. are [`Rgba8Unorm`](TextureFormat::Rgba8Unorm) and
41[`Rgba8UnormSrgb`](TextureFormat::Rgba8UnormSrgb). So what are they, and how
42do they relate to the issues of gamma correction, perceptually-linear "sRGB"
43space, and physically-accurate "linear" space (as described in detail in the
44links above)?
45
46First of all, it will be useful to clarify some terminology, as the words
47"linear" and "sRGB" can be overloaded and lead to confusion:
48
49- **sRGB space** is a set of color primaries — this defines what red, green,
50 and blue are.
51
52In this sense (and perhaps counter-intuitively), texture formats ending in
53"Unorm" and "UnormSrgb" are BOTH in the sRGB color space, and encode their
54data in the same way without losing precision (as might be implied by some
55descriptions of "linear" vs. "sRGB"); more on this later.
56
57Here is some useful terminology to help avoid "linear" and "sRGB" confusion:
58
59- What we traditionally called "linear space" is more accurately called
60 "scene-referred color".
61- What we traditionally called "sRGB space" is more accurately called
62 "monitor-referred color".
63- The conversion from scene-referred color to monitor-referred color is done
64 by the sRGB OETF (opto-electrical transfer function \[light → bits\]).
65- The conversion from monitor-referred color to scene-referred color is done
66 by the sRGB EOTF (electro-optical transfer function \[bits → light\]).
67
68Let's walk our way up the color pipeline. The OS assumes that the bits you
69are sending to the screen are monitor-referred and in the sRGB space (unless
70otherwise specified). It will interpret the bits this way no matter what.
71
72- If you write/shade to a texture which is in a **Unorm** format, it will
73 take the output of your shader and do a float → int conversion, and that's
74 it (Unorm floats are written as ints under the hood, though you don't need
75 to worry about this).
76
77 This means that if you're using this **Unorm** texture as a surface, *the
78 floats you are writing from your shader need to be **monitor-referred*** (i.e.
79 numbers that the OS can safely assume are already in sRGB space).
80
81- If you write/shade to a texture which is in a **UnormSrgb** format, it will
82 take the output of your shader and then apply the sRGB OETF to it, then
83 afterwards do the float → int conversion to store the data.
84
85 This means that if you're using this **UnormSrgb** texture as a surface,
86 *the floats you are writing from your shader need to be **scene-referred***,
87 as the GPU will do the conversion to monitor-referred as part of the write!
88
89The inverse happens when you read from a texture:
90
91- a **Unorm** texture just does int → float on read.
92- a **UnormSrgb** texture applies the sRGB EOTF to do monitor → scene and
93 then does the int → float read.
94
95## Common misconceptions
96
97**Q: So Unorm textures are in "linear" space and UnormSrgb textures are in
98"sRGB" space?**
99
100**A:** No. Despite the naming, you are responsible for whether the outputs of
101your shader represent "scene"- or "monitor"-referred values. The "Srgb" tag on
102the texture format just lets the shader know that you want it to do a
103conversion from sRGB to linear (or, more specifically, "monitor" to "scene")
104when you sample it, or the inverse when you write to it.
105
106## Tips
107
108- Think of texture formats as a combination of data resolution and data
109 conversion functions. For example:
110 [`Rgba8UnormSrgb`](TextureFormat::Rgba8UnormSrgb) = `Rgba8` data +
111 `UnormSrgb` conversion step.
112- Let the GPU do the conversion work for you. Historically in WebGL, for
113 example, if you wanted to do physically-accurate lighting you would need to
114 convert from linear to sRGB manually (often with an approximation like
115 `pow(col, 2.2)`). By being aware of the textures you are sampling and
116 writing to, you should be able to avoid manual conversions.
117*/
118
119use crate::TextureFormat;