Skip to content

Commit b147601

Browse files
jakobhellermanncart
andcommitted
add some more pipelined-rendering shader examples (#3041)
based on #3031 Adds some examples showing of how to use the new pipelined rendering for custom shaders. - a minimal shader example which doesn't use render assets - the same but using glsl - an example showing how to render instanced data - a shader which uses the seconds since startup to animate some textures Instancing shader: ![grafik](https://user-images.githubusercontent.com/22177966/139299294-e176b62a-53d1-4287-9a66-02fb55affc02.png) Animated shader: ![animate_shader](https://user-images.githubusercontent.com/22177966/139299718-2940c0f3-8480-4ee0-98d7-b6ba40dc1472.gif) (the gif makes it look a bit ugly) Co-authored-by: Carter Anderson <[email protected]>
1 parent f3fba09 commit b147601

13 files changed

+1070
-26
lines changed

Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ anyhow = "1.0.4"
102102
rand = "0.8.0"
103103
ron = "0.7.0"
104104
serde = { version = "1", features = ["derive"] }
105+
bytemuck = "1.7"
105106
# Needed to poll Task examples
106107
futures-lite = "1.11.3"
107108

@@ -418,6 +419,22 @@ path = "examples/shader/shader_defs.rs"
418419
name = "shader_material"
419420
path = "examples/shader/shader_material.rs"
420421

422+
[[example]]
423+
name = "shader_material_glsl"
424+
path = "examples/shader/shader_material_glsl.rs"
425+
426+
[[example]]
427+
name = "shader_instancing"
428+
path = "examples/shader/shader_instancing.rs"
429+
430+
[[example]]
431+
name = "animate_shader"
432+
path = "examples/shader/animate_shader.rs"
433+
434+
[[example]]
435+
name = "compute_shader_game_of_life"
436+
path = "examples/shader/compute_shader_game_of_life.rs"
437+
421438
# Tools
422439
[[example]]
423440
name = "bevymark"

assets/shaders/animate_shader.wgsl

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#import bevy_pbr::mesh_view_bind_group
2+
#import bevy_pbr::mesh_struct
3+
4+
[[group(1), binding(0)]]
5+
var<uniform> mesh: Mesh;
6+
7+
struct Vertex {
8+
[[location(0)]] position: vec3<f32>;
9+
[[location(1)]] normal: vec3<f32>;
10+
[[location(2)]] uv: vec2<f32>;
11+
};
12+
13+
struct VertexOutput {
14+
[[builtin(position)]] clip_position: vec4<f32>;
15+
[[location(0)]] uv: vec2<f32>;
16+
};
17+
18+
[[stage(vertex)]]
19+
fn vertex(vertex: Vertex) -> VertexOutput {
20+
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);
21+
22+
var out: VertexOutput;
23+
out.clip_position = view.view_proj * world_position;
24+
out.uv = vertex.uv;
25+
return out;
26+
}
27+
28+
29+
struct Time {
30+
time_since_startup: f32;
31+
};
32+
[[group(2), binding(0)]]
33+
var<uniform> time: Time;
34+
35+
36+
fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
37+
let L = c.x;
38+
let a = c.y;
39+
let b = c.z;
40+
41+
let l_ = L + 0.3963377774 * a + 0.2158037573 * b;
42+
let m_ = L - 0.1055613458 * a - 0.0638541728 * b;
43+
let s_ = L - 0.0894841775 * a - 1.2914855480 * b;
44+
45+
let l = l_*l_*l_;
46+
let m = m_*m_*m_;
47+
let s = s_*s_*s_;
48+
49+
return vec3<f32>(
50+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
51+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
52+
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
53+
);
54+
}
55+
56+
[[stage(fragment)]]
57+
fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
58+
let speed = 2.0;
59+
let t_1 = sin(time.time_since_startup * speed) * 0.5 + 0.5;
60+
let t_2 = cos(time.time_since_startup * speed);
61+
62+
let distance_to_center = distance(in.uv, vec2<f32>(0.5)) * 1.4;
63+
64+
// blending is done in a perceptual color space: https://bottosson.github.io/posts/oklab/
65+
let red = vec3<f32>(0.627955, 0.224863, 0.125846);
66+
let green = vec3<f32>(0.86644, -0.233887, 0.179498);
67+
let blue = vec3<f32>(0.701674, 0.274566, -0.169156);
68+
let white = vec3<f32>(1.0, 0.0, 0.0);
69+
let mixed = mix(mix(red, blue, t_1), mix(green, white, t_2), distance_to_center);
70+
71+
return vec4<f32>(oklab_to_linear_srgb(mixed), 1.0);
72+
}

assets/shaders/custom_material.frag

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#version 450
2+
3+
layout(location = 0) out vec4 o_Target;
4+
5+
layout(set = 1, binding = 0) uniform CustomMaterial {
6+
vec4 Color;
7+
};
8+
9+
void main() {
10+
o_Target = Color;
11+
}

assets/shaders/custom_material.vert

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#version 450
2+
3+
layout(location = 0) in vec3 Vertex_Position;
4+
layout(location = 1) in vec3 Vertex_Normal;
5+
layout(location = 2) in vec2 Vertex_Uv;
6+
7+
layout(set = 0, binding = 0) uniform CameraViewProj {
8+
mat4 ViewProj;
9+
mat4 InverseView;
10+
mat4 Projection;
11+
vec3 WorldPosition;
12+
float near;
13+
float far;
14+
float width;
15+
float height;
16+
};
17+
18+
layout(set = 2, binding = 0) uniform Mesh {
19+
mat4 Model;
20+
mat4 InverseTransposeModel;
21+
uint flags;
22+
};
23+
24+
void main() {
25+
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
26+
}

assets/shaders/game_of_life.wgsl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
[[group(0), binding(0)]]
2+
var texture: texture_storage_2d<rgba8unorm, read_write>;
3+
4+
fn hash(value: u32) -> u32 {
5+
var state = value;
6+
state = state ^ 2747636419u;
7+
state = state * 2654435769u;
8+
state = state ^ state >> 16u;
9+
state = state * 2654435769u;
10+
state = state ^ state >> 16u;
11+
state = state * 2654435769u;
12+
return state;
13+
}
14+
fn randomFloat(value: u32) -> f32 {
15+
return f32(hash(value)) / 4294967295.0;
16+
}
17+
18+
[[stage(compute), workgroup_size(8, 8, 1)]]
19+
fn init([[builtin(global_invocation_id)]] invocation_id: vec3<u32>, [[builtin(num_workgroups)]] num_workgroups: vec3<u32>) {
20+
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
21+
let location_f32 = vec2<f32>(f32(invocation_id.x), f32(invocation_id.y));
22+
23+
let randomNumber = randomFloat(invocation_id.y * num_workgroups.x + invocation_id.x);
24+
let alive = randomNumber > 0.9;
25+
let color = vec4<f32>(f32(alive));
26+
27+
textureStore(texture, location, color);
28+
}
29+
30+
31+
fn get(location: vec2<i32>, offset_x: i32, offset_y: i32) -> i32 {
32+
let value: vec4<f32> = textureLoad(texture, location + vec2<i32>(offset_x, offset_y));
33+
return i32(value.x);
34+
}
35+
36+
fn count_alive(location: vec2<i32>) -> i32 {
37+
return get(location, -1, -1) +
38+
get(location, -1, 0) +
39+
get(location, -1, 1) +
40+
get(location, 0, -1) +
41+
get(location, 0, 1) +
42+
get(location, 1, -1) +
43+
get(location, 1, 0) +
44+
get(location, 1, 1);
45+
}
46+
47+
[[stage(compute), workgroup_size(8, 8, 1)]]
48+
fn update([[builtin(global_invocation_id)]] invocation_id: vec3<u32>) {
49+
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
50+
51+
let n_alive = count_alive(location);
52+
let color = vec4<f32>(f32(n_alive) / 8.0);
53+
54+
var alive: bool;
55+
if (n_alive == 3) {
56+
alive = true;
57+
} else if (n_alive == 2) {
58+
let currently_alive = get(location, 0, 0);
59+
alive = bool(currently_alive);
60+
} else {
61+
alive = false;
62+
}
63+
64+
storageBarrier();
65+
66+
textureStore(texture, location, vec4<f32>(f32(alive)));
67+
}

assets/shaders/hot.frag

Lines changed: 0 additions & 11 deletions
This file was deleted.

assets/shaders/hot.vert

Lines changed: 0 additions & 15 deletions
This file was deleted.

assets/shaders/instancing.wgsl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#import bevy_pbr::mesh_view_bind_group
2+
#import bevy_pbr::mesh_struct
3+
4+
[[group(1), binding(0)]]
5+
var<uniform> mesh: Mesh;
6+
7+
struct Vertex {
8+
[[location(0)]] position: vec3<f32>;
9+
[[location(1)]] normal: vec3<f32>;
10+
[[location(2)]] uv: vec2<f32>;
11+
12+
[[location(3)]] i_pos_scale: vec4<f32>;
13+
[[location(4)]] i_color: vec4<f32>;
14+
};
15+
16+
struct VertexOutput {
17+
[[builtin(position)]] clip_position: vec4<f32>;
18+
[[location(0)]] color: vec4<f32>;
19+
};
20+
21+
[[stage(vertex)]]
22+
fn vertex(vertex: Vertex) -> VertexOutput {
23+
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
24+
let world_position = mesh.model * vec4<f32>(position, 1.0);
25+
26+
var out: VertexOutput;
27+
out.clip_position = view.view_proj * world_position;
28+
out.color = vertex.i_color;
29+
return out;
30+
}
31+
32+
[[stage(fragment)]]
33+
fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
34+
return in.color;
35+
}

examples/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ Example | File | Description
215215
Example | File | Description
216216
--- | --- | ---
217217
`shader_material` | [`shader/shader_material.rs`](./shader/shader_material.rs) | Illustrates creating a custom material and a shader that uses it
218+
`shader_material_glsl` | [`shader/shader_material_glsl.rs`](./shader/shader_material_glsl.rs) | A custom shader using the GLSL shading language.
219+
`shader_instancing` | [`shader/shader_instancing.rs`](./shader/shader_instancing.rs) | A custom shader showing off rendering a mesh multiple times in one draw call.
220+
`animate_shader` | [`shader/animate_shader.rs`](./shader/animate_shader.rs) | Shows how to pass changing data like the time since startup into a shader.
221+
`compute_shader_game_of_life` | [`shader/compute_shader_game_of_life.rs`](./shader/compute_shader_game_of_life.rs) | A compute shader simulating Conway's Game of Life
218222
`shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader)
219223

220224
## Tests

0 commit comments

Comments
 (0)