Skip to content

Commit 1d5d462

Browse files
committed
Externalize all example shaders.
1 parent 467a53d commit 1d5d462

35 files changed

Lines changed: 733 additions & 702 deletions
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
struct Params {
2+
neighbor_dist: f32,
3+
separation_dist: f32,
4+
max_speed: f32,
5+
max_force: f32,
6+
home_radius: f32,
7+
_pad0: f32,
8+
_pad1: f32,
9+
_pad2: f32,
10+
}
11+
12+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
13+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
14+
@group(0) @binding(2) var<storage, read_write> home: array<f32>;
15+
@group(0) @binding(3) var<storage, read_write> steer: array<f32>;
16+
@group(0) @binding(4) var<uniform> params: Params;
17+
18+
fn limit(v: vec3<f32>, max_len: f32) -> vec3<f32> {
19+
let len = length(v);
20+
if len > max_len { return v * (max_len / len); }
21+
return v;
22+
}
23+
24+
// Reynolds: steering = desired - velocity
25+
fn steer_toward(desired: vec3<f32>, vel: vec3<f32>) -> vec3<f32> {
26+
let len = length(desired);
27+
if len < 1e-6 { return vec3<f32>(0.0); }
28+
return limit(desired * (params.max_speed / len) - vel, params.max_force);
29+
}
30+
31+
@compute @workgroup_size(64)
32+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
33+
let i = gid.x;
34+
let count = arrayLength(&position) / 3u;
35+
if i >= count { return; }
36+
37+
let pos = vec3<f32>(position[i * 3u], position[i * 3u + 1u], position[i * 3u + 2u]);
38+
let vel = vec3<f32>(velocity[i * 3u], velocity[i * 3u + 1u], velocity[i * 3u + 2u]);
39+
40+
var separation = vec3<f32>(0.0);
41+
var alignment = vec3<f32>(0.0);
42+
var cohesion = vec3<f32>(0.0);
43+
var separation_count = 0u;
44+
var neighbor_count = 0u;
45+
46+
for (var j = 0u; j < count; j = j + 1u) {
47+
if j == i { continue; }
48+
let other = vec3<f32>(position[j * 3u], position[j * 3u + 1u], position[j * 3u + 2u]);
49+
let d = distance(pos, other);
50+
if d > 0.0 && d < params.separation_dist {
51+
// Point away from the neighbor, weighted by closeness
52+
separation = separation + normalize(pos - other) / d;
53+
separation_count = separation_count + 1u;
54+
}
55+
if d < params.neighbor_dist {
56+
alignment = alignment
57+
+ vec3<f32>(velocity[j * 3u], velocity[j * 3u + 1u], velocity[j * 3u + 2u]);
58+
cohesion = cohesion + other;
59+
neighbor_count = neighbor_count + 1u;
60+
}
61+
}
62+
63+
var force = vec3<f32>(0.0);
64+
if separation_count > 0u {
65+
force = force + steer_toward(separation / f32(separation_count), vel) * 1.5;
66+
}
67+
if neighbor_count > 0u {
68+
force = force + steer_toward(alignment, vel);
69+
force = force + steer_toward(cohesion / f32(neighbor_count) - pos, vel);
70+
}
71+
72+
// The tether: inside home_radius the weight is < 1 and flocking wins;
73+
// past it the pull grows quadratically until it dominates everything.
74+
let home_pos = vec3<f32>(home[i * 3u], home[i * 3u + 1u], home[i * 3u + 2u]);
75+
let to_home = home_pos - pos;
76+
let w = length(to_home) / params.home_radius;
77+
force = force + steer_toward(to_home, vel) * min(w * w, 8.0);
78+
79+
steer[i * 3u] = force.x;
80+
steer[i * 3u + 1u] = force.y;
81+
steer[i * 3u + 2u] = force.z;
82+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
struct Params {
2+
dt: f32,
3+
max_speed: f32,
4+
_pad0: f32,
5+
_pad1: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
10+
@group(0) @binding(2) var<storage, read_write> steer: array<f32>;
11+
@group(0) @binding(3) var<storage, read_write> rotation: array<f32>;
12+
@group(0) @binding(4) var<uniform> params: Params;
13+
14+
// shortest-arc quaternion rotating the mesh's +Z axis onto dir
15+
fn quat_z_to(dir: vec3<f32>) -> vec4<f32> {
16+
let z = vec3<f32>(0.0, 0.0, 1.0);
17+
let d = dot(z, dir);
18+
if d < -0.9999 { return vec4<f32>(0.0, 1.0, 0.0, 0.0); }
19+
return normalize(vec4<f32>(cross(z, dir), 1.0 + d));
20+
}
21+
22+
@compute @workgroup_size(64)
23+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
24+
let i = gid.x;
25+
let count = arrayLength(&position) / 3u;
26+
if i >= count { return; }
27+
28+
var pos = vec3<f32>(position[i * 3u], position[i * 3u + 1u], position[i * 3u + 2u]);
29+
var vel = vec3<f32>(velocity[i * 3u], velocity[i * 3u + 1u], velocity[i * 3u + 2u]);
30+
let force = vec3<f32>(steer[i * 3u], steer[i * 3u + 1u], steer[i * 3u + 2u]);
31+
32+
vel = vel + force * params.dt;
33+
let speed = length(vel);
34+
if speed > params.max_speed { vel = vel * (params.max_speed / speed); }
35+
pos = pos + vel * params.dt;
36+
37+
position[i * 3u] = pos.x;
38+
position[i * 3u + 1u] = pos.y;
39+
position[i * 3u + 2u] = pos.z;
40+
velocity[i * 3u] = vel.x;
41+
velocity[i * 3u + 1u] = vel.y;
42+
velocity[i * 3u + 2u] = vel.z;
43+
44+
if speed > 1e-6 {
45+
let q = quat_z_to(vel / speed);
46+
rotation[i * 4u] = q.x;
47+
rotation[i * 4u + 1u] = q.y;
48+
rotation[i * 4u + 2u] = q.z;
49+
rotation[i * 4u + 3u] = q.w;
50+
}
51+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
struct Params {
2+
neighbor_dist: f32,
3+
separation_dist: f32,
4+
max_speed: f32,
5+
max_force: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
10+
@group(0) @binding(2) var<storage, read_write> steer: array<f32>;
11+
@group(0) @binding(3) var<uniform> params: Params;
12+
13+
fn limit(v: vec3<f32>, max_len: f32) -> vec3<f32> {
14+
let len = length(v);
15+
if len > max_len { return v * (max_len / len); }
16+
return v;
17+
}
18+
19+
// Reynolds: steering = desired - velocity
20+
fn steer_toward(desired: vec3<f32>, vel: vec3<f32>) -> vec3<f32> {
21+
let len = length(desired);
22+
if len < 1e-6 { return vec3<f32>(0.0); }
23+
return limit(desired * (params.max_speed / len) - vel, params.max_force);
24+
}
25+
26+
@compute @workgroup_size(64)
27+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
28+
let i = gid.x;
29+
let count = arrayLength(&position) / 3u;
30+
if i >= count { return; }
31+
32+
let pos = vec3<f32>(position[i * 3u], position[i * 3u + 1u], position[i * 3u + 2u]);
33+
let vel = vec3<f32>(velocity[i * 3u], velocity[i * 3u + 1u], velocity[i * 3u + 2u]);
34+
35+
var separation = vec3<f32>(0.0);
36+
var alignment = vec3<f32>(0.0);
37+
var cohesion = vec3<f32>(0.0);
38+
var separation_count = 0u;
39+
var neighbor_count = 0u;
40+
41+
for (var j = 0u; j < count; j = j + 1u) {
42+
if j == i { continue; }
43+
let other = vec3<f32>(position[j * 3u], position[j * 3u + 1u], position[j * 3u + 2u]);
44+
let d = distance(pos, other);
45+
if d > 0.0 && d < params.separation_dist {
46+
// Point away from the neighbor, weighted by closeness
47+
separation = separation + normalize(pos - other) / d;
48+
separation_count = separation_count + 1u;
49+
}
50+
if d < params.neighbor_dist {
51+
alignment = alignment
52+
+ vec3<f32>(velocity[j * 3u], velocity[j * 3u + 1u], velocity[j * 3u + 2u]);
53+
cohesion = cohesion + other;
54+
neighbor_count = neighbor_count + 1u;
55+
}
56+
}
57+
58+
var force = vec3<f32>(0.0);
59+
if separation_count > 0u {
60+
force = force + steer_toward(separation / f32(separation_count), vel) * 1.5;
61+
}
62+
if neighbor_count > 0u {
63+
force = force + steer_toward(alignment, vel);
64+
force = force + steer_toward(cohesion / f32(neighbor_count) - pos, vel);
65+
}
66+
67+
steer[i * 3u] = force.x;
68+
steer[i * 3u + 1u] = force.y;
69+
steer[i * 3u + 2u] = force.z;
70+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
struct Params {
2+
dt: f32,
3+
max_speed: f32,
4+
bound: f32,
5+
_pad: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
10+
@group(0) @binding(2) var<storage, read_write> steer: array<f32>;
11+
@group(0) @binding(3) var<storage, read_write> rotation: array<f32>;
12+
@group(0) @binding(4) var<uniform> params: Params;
13+
14+
// shortest-arc quaternion rotating the mesh's +Z axis onto dir
15+
fn quat_z_to(dir: vec3<f32>) -> vec4<f32> {
16+
let z = vec3<f32>(0.0, 0.0, 1.0);
17+
let d = dot(z, dir);
18+
if d < -0.9999 { return vec4<f32>(0.0, 1.0, 0.0, 0.0); }
19+
return normalize(vec4<f32>(cross(z, dir), 1.0 + d));
20+
}
21+
22+
@compute @workgroup_size(64)
23+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
24+
let i = gid.x;
25+
let count = arrayLength(&position) / 3u;
26+
if i >= count { return; }
27+
28+
var pos = vec3<f32>(position[i * 3u], position[i * 3u + 1u], position[i * 3u + 2u]);
29+
var vel = vec3<f32>(velocity[i * 3u], velocity[i * 3u + 1u], velocity[i * 3u + 2u]);
30+
let force = vec3<f32>(steer[i * 3u], steer[i * 3u + 1u], steer[i * 3u + 2u]);
31+
32+
vel = vel + force * params.dt;
33+
let speed = length(vel);
34+
if speed > params.max_speed { vel = vel * (params.max_speed / speed); }
35+
pos = pos + vel * params.dt;
36+
37+
// wrap into [-bound, bound]: ((p + b) mod 2b + 2b) mod 2b - b
38+
let span = 2.0 * params.bound;
39+
pos = ((pos + params.bound) % span + span) % span - params.bound;
40+
41+
position[i * 3u] = pos.x;
42+
position[i * 3u + 1u] = pos.y;
43+
position[i * 3u + 2u] = pos.z;
44+
velocity[i * 3u] = vel.x;
45+
velocity[i * 3u + 1u] = vel.y;
46+
velocity[i * 3u + 2u] = vel.z;
47+
48+
if speed > 1e-6 {
49+
let q = quat_z_to(vel / speed);
50+
rotation[i * 4u] = q.x;
51+
rotation[i * 4u + 1u] = q.y;
52+
rotation[i * 4u + 2u] = q.z;
53+
rotation[i * 4u + 3u] = q.w;
54+
}
55+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
struct Params {
2+
dt: f32,
3+
}
4+
5+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
6+
@group(0) @binding(1) var<uniform> params: Params;
7+
8+
@compute @workgroup_size(64)
9+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
10+
let i = gid.x;
11+
let count = arrayLength(&position) / 3u;
12+
if i >= count {
13+
return;
14+
}
15+
let cs = cos(params.dt);
16+
let sn = sin(params.dt);
17+
let x = position[i * 3u + 0u];
18+
let z = position[i * 3u + 2u];
19+
position[i * 3u + 0u] = x * cs - z * sn;
20+
position[i * 3u + 2u] = x * sn + z * cs;
21+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
struct Params {
2+
dt: f32,
3+
ttl: f32,
4+
gravity: f32,
5+
_pad: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
10+
@group(0) @binding(2) var<storage, read_write> scale: array<f32>;
11+
@group(0) @binding(3) var<storage, read_write> age: array<f32>;
12+
@group(0) @binding(4) var<storage, read_write> life: array<f32>;
13+
@group(0) @binding(5) var<uniform> params: Params;
14+
15+
@compute @workgroup_size(64)
16+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
17+
let i = gid.x;
18+
let count = arrayLength(&age);
19+
if i >= count { return; }
20+
if life[i] <= 0.0 { return; }
21+
22+
age[i] = age[i] + params.dt;
23+
24+
velocity[i * 3u + 1u] = velocity[i * 3u + 1u] - params.gravity * params.dt;
25+
26+
position[i * 3u + 0u] = position[i * 3u + 0u] + velocity[i * 3u + 0u] * params.dt;
27+
position[i * 3u + 1u] = position[i * 3u + 1u] + velocity[i * 3u + 1u] * params.dt;
28+
position[i * 3u + 2u] = position[i * 3u + 2u] + velocity[i * 3u + 2u] * params.dt;
29+
30+
let remaining = clamp(1.0 - age[i] / params.ttl, 0.0, 1.0);
31+
let s = remaining * remaining;
32+
scale[i * 3u + 0u] = s;
33+
scale[i * 3u + 1u] = s;
34+
scale[i * 3u + 2u] = s;
35+
36+
if age[i] > params.ttl { life[i] = 0.0; }
37+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
struct Spawn {
2+
pos: vec4<f32>,
3+
speed: vec4<f32>,
4+
}
5+
6+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
7+
@group(0) @binding(1) var<storage, read_write> velocity: array<f32>;
8+
@group(0) @binding(2) var<storage, read_write> color: array<f32>;
9+
@group(0) @binding(3) var<storage, read_write> scale: array<f32>;
10+
@group(0) @binding(4) var<storage, read_write> age: array<f32>;
11+
@group(0) @binding(5) var<storage, read_write> life: array<f32>;
12+
@group(0) @binding(6) var<uniform> spawn: Spawn;
13+
@group(0) @binding(7) var<uniform> emit_base: u32;
14+
@group(0) @binding(8) var<uniform> emit_count: u32;
15+
@group(0) @binding(9) var<uniform> emit_capacity: u32;
16+
17+
fn hash(n: u32) -> u32 {
18+
var x = n;
19+
x = (x ^ 61u) ^ (x >> 16u);
20+
x = x + (x << 3u);
21+
x = x ^ (x >> 4u);
22+
x = x * 0x27d4eb2du;
23+
x = x ^ (x >> 15u);
24+
return x;
25+
}
26+
27+
fn hash_unit(n: u32) -> f32 {
28+
return f32(hash(n)) / f32(0xffffffffu);
29+
}
30+
31+
@compute @workgroup_size(64)
32+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
33+
let local_i = gid.x;
34+
if local_i >= emit_count { return; }
35+
let slot = (emit_base + local_i) % emit_capacity;
36+
37+
let seed = emit_base + local_i;
38+
39+
let theta = hash_unit(seed) * 6.2831853;
40+
let r = sqrt(hash_unit(seed * 2u + 1u));
41+
let dirxz = vec2<f32>(cos(theta), sin(theta)) * r;
42+
let dy = 0.7 + 0.3 * hash_unit(seed * 3u + 7u);
43+
let v = vec3<f32>(dirxz.x, dy, dirxz.y) * spawn.speed.x;
44+
45+
position[slot * 3u + 0u] = spawn.pos.x;
46+
position[slot * 3u + 1u] = spawn.pos.y;
47+
position[slot * 3u + 2u] = spawn.pos.z;
48+
49+
velocity[slot * 3u + 0u] = v.x;
50+
velocity[slot * 3u + 1u] = v.y;
51+
velocity[slot * 3u + 2u] = v.z;
52+
53+
let h = fract(hash_unit(seed * 5u + 11u));
54+
color[slot * 4u + 0u] = 0.5 + 0.5 * sin(h * 6.28);
55+
color[slot * 4u + 1u] = 0.5 + 0.5 * sin(h * 6.28 + 2.094);
56+
color[slot * 4u + 2u] = 0.5 + 0.5 * sin(h * 6.28 + 4.189);
57+
color[slot * 4u + 3u] = 1.0;
58+
59+
scale[slot * 3u + 0u] = 1.0;
60+
scale[slot * 3u + 1u] = 1.0;
61+
scale[slot * 3u + 2u] = 1.0;
62+
63+
age[slot] = 0.0;
64+
life[slot] = 1.0;
65+
}

0 commit comments

Comments
 (0)