main 3a820d6

capture_sdr.frag RawHistoryFolder

1#version 450
2// Capture-only SDR conversion of the HDR composition image (linear BT.709
3// in units of SDR reference white, values beyond [0, 1] on both sides).
4// Presentation never uses this pass. The mapping is deterministic and
5// mirrored by captureCurve in capture.go for tests:
6//
7// 1. Gamut: a component below zero (a BT.2020 color outside BT.709) is
8//    brought to zero by mixing the pixel toward its own luminance: hue
9//    direction and luminance are kept, chroma is reduced just enough.
10// 2. Shoulder, on the largest component so hue is kept: identity up to
11//    the knee k = 0.9 (SDR content below 90 % white is exact), then the
12//    rational curve k + (1 - k) * t / (1 + t) with t = (m - k) / s,
13//    s = 0.5, which reaches 1 only asymptotically: diffuse white lands
14//    at 0.917 (sRGB 245), 2x white at 251, 5x (1000 nits) at 254; the
15//    order of highlights is kept instead of clipping.
16// 3. sRGB encoding, opaque.
17layout(set=0, binding=0) uniform sampler2D imageHDR;
18layout(location=0) out vec4 color;
19
20const float knee = 0.9;
21const float scale = 0.5;
22const vec3 luma709 = vec3(0.2126, 0.7152, 0.0722);
23
24float shoulder(float m) {
25    float t = (m - knee) / scale;
26    return m <= knee ? m : knee + (1.0 - knee) * t / (1.0 + t);
27}
28
29vec3 encodeSRGB(vec3 v) {
30    return mix(v * 12.92, 1.055 * pow(v, vec3(1.0 / 2.4)) - 0.055, greaterThan(v, vec3(0.0031308)));
31}
32
33void main() {
34    vec2 uv = gl_FragCoord.xy / vec2(textureSize(imageHDR, 0));
35    vec3 c = texture(imageHDR, uv).rgb;
36    // NaN is black before any clamp (clamping NaN is undefined);
37    // infinities are bounded to the half range.
38    c = mix(c, vec3(0.0), isnan(c));
39    c = clamp(c, vec3(-65504.0), vec3(65504.0));
40    float y = max(dot(c, luma709), 0.0);
41    float lo = min(min(c.r, c.g), c.b);
42    if (lo < 0.0) {
43        // c + t * (y - c) == 0 for the lowest component: t = -lo / (y - lo).
44        float t = -lo / max(y - lo, 1e-6);
45        c = mix(c, vec3(y), clamp(t, 0.0, 1.0));
46    }
47    c = max(c, vec3(0.0));
48    float m = max(max(c.r, c.g), c.b);
49    if (m > knee) {
50        c *= shoulder(m) / m;
51    }
52    color = vec4(encodeSRGB(clamp(c, vec3(0.0), vec3(1.0))), 1.0);
53}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close