Skip to content

Commit f6a9c4b

Browse files
committed
Particle updates
1 parent 1d5d462 commit f6a9c4b

66 files changed

Lines changed: 4803 additions & 553 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 323 additions & 261 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ path = "examples/camera_controllers.rs"
212212
name = "compute_readback"
213213
path = "examples/compute_readback.rs"
214214

215+
[[example]]
216+
name = "alias_spike"
217+
path = "examples/alias_spike.rs"
218+
215219
[[example]]
216220
name = "particles_basic"
217221
path = "examples/particles_basic.rs"

assets/shaders/density_color.wesl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
struct Params {
2+
scale: f32,
3+
}
4+
5+
@group(0) @binding(0) var<storage, read> density: array<f32>;
6+
@group(0) @binding(1) var<storage, read_write> color: array<f32>;
7+
@group(0) @binding(2) var<uniform> params: Params;
8+
9+
@compute @workgroup_size(64)
10+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
11+
let i = gid.x;
12+
if i >= arrayLength(&density) { return; }
13+
14+
let t = clamp(density[i] * params.scale, 0.0, 1.0);
15+
let cool = vec3<f32>(0.10, 0.22, 0.70);
16+
let warm = vec3<f32>(1.00, 0.55, 0.15);
17+
let rgb = mix(cool, warm, t) * (0.35 + 1.1 * t);
18+
19+
color[i * 4u + 0u] = rgb.x;
20+
color[i * 4u + 1u] = rgb.y;
21+
color[i * 4u + 2u] = rgb.z;
22+
color[i * 4u + 3u] = 1.0;
23+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
struct Params {
2+
nx: u32,
3+
ny: u32,
4+
time: f32,
5+
extent: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> color: array<f32>;
10+
@group(0) @binding(2) var<storage, read_write> normal: array<f32>;
11+
@group(0) @binding(3) var<storage, read_write> indices: array<u32>;
12+
@group(0) @binding(4) var<uniform> params: Params;
13+
14+
fn height(x: f32, z: f32, t: f32) -> f32 {
15+
let r = sqrt(x * x + z * z);
16+
return sin(r * 0.8 - t * 2.0) * 1.6 * exp(-r * 0.10)
17+
+ sin(x * 0.5 + t) * 0.4
18+
+ cos(z * 0.6 - t * 1.3) * 0.3;
19+
}
20+
21+
@compute @workgroup_size(64)
22+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
23+
let i = gid.x;
24+
let nx = params.nx;
25+
let ny = params.ny;
26+
if i >= nx * ny { return; }
27+
28+
let gx = i % nx;
29+
let gy = i / nx;
30+
let x = (f32(gx) / f32(nx - 1u) - 0.5) * params.extent;
31+
let z = (f32(gy) / f32(ny - 1u) - 0.5) * params.extent;
32+
let t = params.time;
33+
34+
let h = height(x, z, t);
35+
position[i * 3u + 0u] = x;
36+
position[i * 3u + 1u] = h;
37+
position[i * 3u + 2u] = z;
38+
39+
let e = 0.12;
40+
let hl = height(x - e, z, t);
41+
let hr = height(x + e, z, t);
42+
let hd = height(x, z - e, t);
43+
let hu = height(x, z + e, t);
44+
let n = normalize(vec3<f32>(hl - hr, 2.0 * e, hd - hu));
45+
normal[i * 3u + 0u] = n.x;
46+
normal[i * 3u + 1u] = n.y;
47+
normal[i * 3u + 2u] = n.z;
48+
49+
let c = clamp(h * 0.35 + 0.5, 0.0, 1.0);
50+
color[i * 4u + 0u] = 0.15 + 0.75 * c;
51+
color[i * 4u + 1u] = 0.30 + 0.45 * (1.0 - abs(c - 0.5) * 2.0);
52+
color[i * 4u + 2u] = 0.95 - 0.65 * c;
53+
color[i * 4u + 3u] = 1.0;
54+
55+
if gx + 1u < nx && gy + 1u < ny {
56+
let cell = gy * (nx - 1u) + gx;
57+
let base = cell * 6u;
58+
indices[base + 0u] = i;
59+
indices[base + 1u] = i + 1u;
60+
indices[base + 2u] = i + nx + 1u;
61+
indices[base + 3u] = i;
62+
indices[base + 4u] = i + nx + 1u;
63+
indices[base + 5u] = i + nx;
64+
}
65+
}

assets/shaders/gen_surface.wesl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
struct Params {
2+
nx: u32,
3+
ny: u32,
4+
time: f32,
5+
extent: f32,
6+
}
7+
8+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
9+
@group(0) @binding(1) var<storage, read_write> indices: array<u32>;
10+
@group(0) @binding(2) var<uniform> params: Params;
11+
12+
fn wave(p: vec2<f32>, dir: vec2<f32>, freq: f32, speed: f32, t: f32) -> f32 {
13+
return sin(dot(p, normalize(dir)) * freq + t * speed);
14+
}
15+
16+
@compute @workgroup_size(64)
17+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
18+
let i = gid.x;
19+
let nx = params.nx;
20+
let ny = params.ny;
21+
if i >= nx * ny { return; }
22+
23+
let gx = i % nx;
24+
let gy = i / nx;
25+
26+
let u = f32(gx) / f32(nx - 1u);
27+
let v = f32(gy) / f32(ny - 1u);
28+
let x = (u - 0.5) * params.extent;
29+
let z = (v - 0.5) * params.extent;
30+
31+
let t = params.time;
32+
let p = vec2(x, z);
33+
34+
let w1 = wave(p, vec2(1.0, 0.3), 0.6, 0.7, t) * 1.0;
35+
let w2 = wave(p, vec2(0.4, 1.0), 0.9, 0.9, t) * 0.6;
36+
let w3 = wave(p, vec2(-0.7, 0.6), 1.7, 1.4, t) * 0.3;
37+
let w4 = wave(p, vec2(0.9, -0.5), 2.3, 1.8, t) * 0.18;
38+
let w5 = wave(p, vec2(-0.2, -1.0), 4.1, 2.6, t) * 0.08;
39+
var h = w1 + w2 + w3 + w4 + w5;
40+
h = h + 0.25 * h * abs(h);
41+
42+
position[i * 3u + 0u] = x;
43+
position[i * 3u + 1u] = h;
44+
position[i * 3u + 2u] = z;
45+
46+
if gx + 1u < nx && gy + 1u < ny {
47+
let cell = gy * (nx - 1u) + gx;
48+
let base = cell * 6u;
49+
let v00 = i;
50+
let v10 = i + 1u;
51+
let v01 = i + nx;
52+
let v11 = i + nx + 1u;
53+
indices[base + 0u] = v00;
54+
indices[base + 1u] = v10;
55+
indices[base + 2u] = v11;
56+
indices[base + 3u] = v00;
57+
indices[base + 4u] = v11;
58+
indices[base + 5u] = v01;
59+
}
60+
}

assets/shaders/plexus_curve.wesl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { lygia::color::space::hsv2rgb::hsv2rgb };
2+
3+
struct Params {
4+
count: u32,
5+
time: f32,
6+
fx: f32, // frequency ratios
7+
fy: f32,
8+
fz: f32,
9+
loops: f32, // parameter spans loops * TAU
10+
scale: f32,
11+
hue_mix: f32, // 0 = grayscale (black), 1 = rainbow
12+
}
13+
14+
@group(0) @binding(0) var<storage, read_write> position: array<f32>;
15+
@group(0) @binding(1) var<storage, read_write> color: array<f32>;
16+
@group(0) @binding(2) var<uniform> params: Params;
17+
18+
const TAU: f32 = 6.28318530718;
19+
20+
@compute @workgroup_size(64)
21+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
22+
let i = gid.x;
23+
if i >= params.count { return; }
24+
25+
let u = f32(i) / f32(params.count);
26+
let t = u * TAU * params.loops;
27+
let world = vec3<f32>(
28+
sin(params.fx * t + params.time * 0.30),
29+
sin(params.fy * t + params.time * 0.47),
30+
sin(params.fz * t + params.time * 0.23),
31+
) * params.scale;
32+
position[i * 3u + 0u] = world.x;
33+
position[i * 3u + 1u] = world.y;
34+
position[i * 3u + 2u] = world.z;
35+
36+
let rainbow = hsv2rgb(vec3<f32>(fract(u + params.time * 0.03), 0.75, 1.0));
37+
let rgb = mix(vec3<f32>(0.0), rainbow, params.hue_mix);
38+
color[i * 4u + 0u] = rgb.x;
39+
color[i * 4u + 1u] = rgb.y;
40+
color[i * 4u + 2u] = rgb.z;
41+
color[i * 4u + 3u] = 1.0;
42+
}

assets/shaders/plexus_link.wesl

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
struct GridParams {
2+
grid_min: vec3<f32>,
3+
cell_size: f32,
4+
dims_x: u32,
5+
dims_y: u32,
6+
dims_z: u32,
7+
_pad: u32,
8+
}
9+
10+
struct Params {
11+
connection_radius: f32,
12+
connection_ramp: f32, // falloff exponent
13+
line_alpha: f32, // opacity scale
14+
max_links: u32, // per-particle edge cap
15+
}
16+
17+
// Auto-bound by apply(): names must be position/color.
18+
@group(0) @binding(0) var<storage, read> position: array<f32>;
19+
@group(0) @binding(1) var<storage, read> color: array<f32>;
20+
@group(0) @binding(2) var<storage, read_write> edge_pos: array<f32>;
21+
@group(0) @binding(3) var<storage, read_write> edge_col: array<f32>;
22+
@group(0) @binding(4) var<storage, read_write> indices: array<u32>;
23+
@group(0) @binding(5) var<storage, read_write> draw_args: array<atomic<u32>>;
24+
// Bound by grid.bind().
25+
@group(0) @binding(6) var<storage, read> offsets: array<u32>;
26+
@group(0) @binding(7) var<storage, read> sorted: array<u32>;
27+
@group(0) @binding(8) var<uniform> params: Params;
28+
@group(0) @binding(9) var<uniform> gp: GridParams;
29+
30+
fn cell_coords(p: vec3<f32>, grid_min: vec3<f32>, cell_size: f32, dims: vec3<u32>) -> vec3<i32> {
31+
let rel = (p - grid_min) / cell_size;
32+
return vec3<i32>(
33+
clamp(i32(floor(rel.x)), 0, i32(dims.x) - 1),
34+
clamp(i32(floor(rel.y)), 0, i32(dims.y) - 1),
35+
clamp(i32(floor(rel.z)), 0, i32(dims.z) - 1),
36+
);
37+
}
38+
39+
fn cell_index(c: vec3<u32>, dims: vec3<u32>) -> u32 {
40+
return c.x + c.y * dims.x + c.z * dims.x * dims.y;
41+
}
42+
43+
fn load_pos(i: u32) -> vec3<f32> {
44+
return vec3<f32>(position[i * 3u], position[i * 3u + 1u], position[i * 3u + 2u]);
45+
}
46+
47+
fn load_rgb(i: u32) -> vec3<f32> {
48+
return vec3<f32>(color[i * 4u], color[i * 4u + 1u], color[i * 4u + 2u]);
49+
}
50+
51+
fn emit_vertex(slot: u32, p: vec3<f32>, rgb: vec3<f32>, a: f32) {
52+
edge_pos[slot * 3u + 0u] = p.x;
53+
edge_pos[slot * 3u + 1u] = p.y;
54+
edge_pos[slot * 3u + 2u] = p.z;
55+
edge_col[slot * 4u + 0u] = rgb.x;
56+
edge_col[slot * 4u + 1u] = rgb.y;
57+
edge_col[slot * 4u + 2u] = rgb.z;
58+
edge_col[slot * 4u + 3u] = a;
59+
indices[slot] = slot;
60+
}
61+
62+
@compute @workgroup_size(64)
63+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
64+
let i = gid.x;
65+
let count = arrayLength(&position) / 3u;
66+
if i >= count { return; }
67+
68+
let cap = arrayLength(&indices);
69+
let pos = load_pos(i);
70+
let rgb = load_rgb(i);
71+
let radius = params.connection_radius;
72+
let r2 = radius * radius;
73+
let dims = vec3<u32>(gp.dims_x, gp.dims_y, gp.dims_z);
74+
let base = cell_coords(pos, gp.grid_min, gp.cell_size, dims);
75+
76+
let reach = max(1, i32(ceil(radius / gp.cell_size)));
77+
let z0 = max(base.z - reach, 0);
78+
let z1 = min(base.z + reach, i32(gp.dims_z) - 1);
79+
let y0 = max(base.y - reach, 0);
80+
let y1 = min(base.y + reach, i32(gp.dims_y) - 1);
81+
let x0 = max(base.x - reach, 0);
82+
let x1 = min(base.x + reach, i32(gp.dims_x) - 1);
83+
84+
var emitted = 0u;
85+
for (var cz = z0; cz <= z1; cz++) {
86+
for (var cy = y0; cy <= y1; cy++) {
87+
for (var cx = x0; cx <= x1; cx++) {
88+
let cell = cell_index(vec3<u32>(u32(cx), u32(cy), u32(cz)), dims);
89+
let start = offsets[cell];
90+
let end = offsets[cell + 1u];
91+
for (var s = start; s < end; s++) {
92+
let j = sorted[s];
93+
if j <= i { continue; } // one direction per edge
94+
let pj = load_pos(j);
95+
let diff = pos - pj;
96+
let d2 = dot(diff, diff);
97+
if d2 > r2 { continue; }
98+
99+
if emitted >= params.max_links { return; }
100+
emitted += 1u;
101+
102+
let d = sqrt(d2);
103+
let a = pow(1.0 / (d / radius + 1.0), params.connection_ramp) * params.line_alpha;
104+
105+
let slot = atomicAdd(&draw_args[0], 2u);
106+
if slot + 1u < cap {
107+
emit_vertex(slot, pos, rgb, a);
108+
emit_vertex(slot + 1u, pj, load_rgb(j), a);
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}

crates/processing_core/src/constants.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,68 @@ pub const LAB: &str = "lab";
5555
pub const LCH: &str = "lch";
5656
pub const XYZ: &str = "xyz";
5757

58+
pub const MAP: &str = "map";
59+
pub const COMBINE: &str = "combine";
60+
pub const MIX: &str = "mix";
61+
pub const LOOKUP: &str = "lookup";
62+
pub const REDUCE: &str = "reduce";
63+
pub const EXTRACT: &str = "extract";
64+
pub const PACK: &str = "pack";
65+
pub const GENERATE: &str = "generate";
66+
pub const NEIGHBOR: &str = "neighbor";
67+
68+
pub const COUNT: &str = "count";
69+
pub const DENSITY: &str = "density";
70+
71+
pub const CONSTANT: &str = "constant";
72+
pub const SMOOTHSTEP: &str = "smoothstep";
73+
pub const QUADRATIC: &str = "quadratic";
74+
pub const CUBIC: &str = "cubic";
75+
pub const INVERSE: &str = "inverse";
76+
77+
pub const AFFINE: &str = "affine";
78+
pub const ABS: &str = "abs";
79+
pub const NEGATE: &str = "negate";
80+
pub const FLOOR: &str = "floor";
81+
pub const SQRT: &str = "sqrt";
82+
pub const GREATER: &str = "greater";
83+
pub const LESS: &str = "less";
84+
pub const GEQ: &str = "geq";
85+
pub const LEQ: &str = "leq";
86+
pub const EQ: &str = "eq";
87+
pub const NEQ: &str = "neq";
88+
89+
pub const ADD: &str = "add";
90+
pub const SUB: &str = "sub";
91+
pub const MUL: &str = "mul";
92+
pub const DIV: &str = "div";
93+
pub const POW: &str = "pow";
94+
95+
pub const LENGTH: &str = "length";
96+
pub const SUM: &str = "sum";
97+
pub const SUMSQ: &str = "sumsq";
98+
pub const MEAN: &str = "mean";
99+
pub const MIN: &str = "min";
100+
pub const MAX: &str = "max";
101+
102+
pub const UNIFORM: &str = "uniform";
103+
pub const SIGNED: &str = "signed";
104+
pub const GAUSSIAN: &str = "gaussian";
105+
106+
pub const NOISE: &str = "noise";
107+
pub const TRANSFORM: &str = "transform";
108+
pub const ATTRACT: &str = "attract";
109+
pub const DRAG: &str = "drag";
110+
pub const VORTEX: &str = "vortex";
111+
pub const FORCE: &str = "force";
112+
pub const INTEGRATE: &str = "integrate";
113+
pub const AGE: &str = "age";
114+
pub const IMPULSE: &str = "impulse";
115+
pub const ORIENT: &str = "orient";
116+
pub const FIELD: &str = "field";
117+
pub const BOUNDS_SPHERE: &str = "bounds_sphere";
118+
pub const BOUNDS_BOX: &str = "bounds_box";
119+
58120
pub const PI: f32 = std::f32::consts::PI;
59121
pub const TWO_PI: f32 = std::f32::consts::TAU;
60122
pub const HALF_PI: f32 = std::f32::consts::FRAC_PI_2;

0 commit comments

Comments
 (0)