Skip to content

Commit f59900b

Browse files
authored
Use srgb for offscreen canvases (#288)
* Use srgb for offscreen canvases * Fix triangle example to be a perceptually linear gradient * Update example screenshot * other example also changed * Make cube example more srgb-aware * update cube screenshot again * tweak triangle example colors * update triangle screenshot
1 parent 7dcd6ae commit f59900b

File tree

7 files changed

+29
-20
lines changed

7 files changed

+29
-20
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
A Python implementation of WebGPU - the next generation GPU API.
88

9-
<img width=300 src='https://user-images.githubusercontent.com/3015475/159725890-5656204b-648b-4f6f-88d5-a656d8ad00c6.png' />
10-
<img width=300 src='https://user-images.githubusercontent.com/3015475/159726895-2c0cbc7b-ed0a-4cd3-a90c-dd579977eaa4.png' />
9+
<img width=300 src='https://raw.githubusercontent.com/pygfx/wgpu-py/main/examples/screenshots/cube.png' />
10+
<img width=300 src='https://raw.githubusercontent.com/pygfx/wgpu-py/main/examples/screenshots/triangle_auto.png' />
1111

1212

1313
## Introduction

examples/cube.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
).flatten()
8080

8181

82+
# Create texture data (srgb gray values)
8283
texture_data = np.array(
8384
[
8485
[50, 100, 150, 200],
@@ -181,7 +182,8 @@
181182
@stage(fragment)
182183
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
183184
let value = textureSample(r_tex, r_sampler, in.texcoord).r;
184-
return vec4<f32>(value, value, value, 1.0);
185+
let physical_color = vec3<f32>(pow(value, 2.2)); // gamma correct
186+
return vec4<f32>(physical_color.rgb, 1.0);
185187
}
186188
"""
187189

@@ -357,7 +359,7 @@ def draw_frame():
357359
{
358360
"view": current_texture_view,
359361
"resolve_target": None,
360-
"clear_value": (0.1, 0.3, 0.2, 1),
362+
"clear_value": (1, 1, 1, 1),
361363
"load_op": wgpu.LoadOp.clear,
362364
"store_op": wgpu.StoreOp.store,
363365
}

examples/screenshots/cube.png

-15 Bytes
Loading
17.4 KB
Loading

examples/triangle.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,27 @@
3232
3333
@stage(vertex)
3434
fn vs_main(in: VertexInput) -> VertexOutput {
35-
var positions = array<vec2<f32>, 3>(vec2<f32>(0.0, -0.5), vec2<f32>(0.5, 0.5), vec2<f32>(-0.5, 0.7));
35+
var positions = array<vec2<f32>, 3>(
36+
vec2<f32>(0.0, -0.5),
37+
vec2<f32>(0.5, 0.5),
38+
vec2<f32>(-0.5, 0.75),
39+
);
40+
var colors = array<vec3<f32>, 3>( // srgb colors
41+
vec3<f32>(1.0, 1.0, 0.0),
42+
vec3<f32>(1.0, 0.0, 1.0),
43+
vec3<f32>(0.0, 1.0, 1.0),
44+
);
3645
let index = i32(in.vertex_index);
37-
let p: vec2<f32> = positions[index];
38-
3946
var out: VertexOutput;
40-
out.pos = vec4<f32>(p, 0.0, 1.0);
41-
out.color = vec4<f32>(p, 0.5, 1.0);
47+
out.pos = vec4<f32>(positions[index], 0.0, 1.0);
48+
out.color = vec4<f32>(colors[index], 1.0);
4249
return out;
4350
}
4451
4552
@stage(fragment)
4653
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
47-
return in.color;
54+
let physical_color = pow(in.color.rgb, vec3<f32>(2.2)); // gamma correct
55+
return vec4<f32>(physical_color, in.color.a);
4856
}
4957
"""
5058

wgpu/gui/_offscreen.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ def present(self, texture_view):
3636
def get_preferred_format(self):
3737
"""Get the preferred format for this canvas. This method can
3838
be overloaded to control the used texture format. The default
39-
is "rgba8unorm" (not including srgb colormapping).
39+
is "rgba8unorm-srgb".
4040
"""
4141
# Use rgba because that order is more common for processing and storage.
42-
# Use 8unorm because 8bit is enough and common in most cases.
43-
# We DO NOT use srgb colormapping here; we return the "raw" output.
44-
return "rgba8unorm"
42+
# Use srgb because that's what how colors are usually expected to be.
43+
# Use 8unorm because 8bit is enough (when using srgb).
44+
return "rgba8unorm-srgb"
4545

4646

4747
class GPUCanvasContextOffline(base.GPUCanvasContext):
@@ -63,7 +63,7 @@ def get_preferred_format(self, adapter):
6363
if canvas:
6464
return canvas.get_preferred_format()
6565
else:
66-
return "rgba8unorm"
66+
return "rgba8unorm-srgb"
6767

6868
def get_current_texture(self):
6969
self._create_new_texture_if_needed()

wgpu/gui/jupyter.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,10 @@ def present(self, texture_view):
111111
return np.frombuffer(data, np.uint8).reshape(size[1], size[0], 4)
112112

113113
def get_preferred_format(self):
114-
# Use a format that maps well to PNG: rgba8norm.
115-
# Use srgb for perseptive color mapping. You probably want to
116-
# apply this before displaying on screen, but you do not want
117-
# to duplicate it. When a PNG is shown on screen in the browser
118-
# it's shown as-is (at least it was when I just tried).
114+
# Use a format that maps well to PNG: rgba8norm. Use srgb for
115+
# perseptive color mapping. This is the common colorspace for
116+
# e.g. png and jpg images. Most tools (browsers included) will
117+
# blit the png to screen as-is, and a screen wants colors in srgb.
119118
return "rgba8unorm-srgb"
120119

121120

0 commit comments

Comments
 (0)