blob: c87a8cafd8d64664b123a48e38b36753b81f2291 [file] [log] [blame]
Ben Claytonbe2362b2022-01-18 18:58:16 +00001////////////////////////////////////////////////////////////////////////////////
2// Utilities
3////////////////////////////////////////////////////////////////////////////////
4var<private> rand_seed : vec2<f32>;
5
6fn rand() -> f32 {
7 rand_seed.x = fract(cos(dot(rand_seed, vec2<f32>(23.14077926, 232.61690225))) * 136.8168);
8 rand_seed.y = fract(cos(dot(rand_seed, vec2<f32>(54.47856553, 345.84153136))) * 534.7645);
9 return rand_seed.y;
10}
11
12////////////////////////////////////////////////////////////////////////////////
13// Vertex shader
14////////////////////////////////////////////////////////////////////////////////
15struct RenderParams {
James Price3b671cb2022-03-28 14:31:22 +000016 modelViewProjectionMatrix : mat4x4<f32>,
17 right : vec3<f32>,
18 up : vec3<f32>,
Ben Claytonbe2362b2022-01-18 18:58:16 +000019};
Ben Clayton01e4b6f2022-01-19 22:46:57 +000020@binding(0) @group(0) var<uniform> render_params : RenderParams;
Ben Claytonbe2362b2022-01-18 18:58:16 +000021
22struct VertexInput {
James Price3b671cb2022-03-28 14:31:22 +000023 @location(0) position : vec3<f32>,
24 @location(1) color : vec4<f32>,
25 @location(2) quad_pos : vec2<f32>, // -1..+1
Ben Claytonbe2362b2022-01-18 18:58:16 +000026};
27
28struct VertexOutput {
James Price3b671cb2022-03-28 14:31:22 +000029 @builtin(position) position : vec4<f32>,
30 @location(0) color : vec4<f32>,
31 @location(1) quad_pos : vec2<f32>, // -1..+1
Ben Claytonbe2362b2022-01-18 18:58:16 +000032};
33
dan sinclairb29892b2022-06-07 13:55:34 +000034@vertex
Ben Claytonbe2362b2022-01-18 18:58:16 +000035fn vs_main(in : VertexInput) -> VertexOutput {
36 var quad_pos = mat2x3<f32>(render_params.right, render_params.up) * in.quad_pos;
37 var position = in.position + quad_pos * 0.01;
38 var out : VertexOutput;
39 out.position = render_params.modelViewProjectionMatrix * vec4<f32>(position, 1.0);
40 out.color = in.color;
41 out.quad_pos = in.quad_pos;
42 return out;
43}
44
45////////////////////////////////////////////////////////////////////////////////
46// Fragment shader
47////////////////////////////////////////////////////////////////////////////////
dan sinclairb29892b2022-06-07 13:55:34 +000048@fragment
Ben Clayton01e4b6f2022-01-19 22:46:57 +000049fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
Ben Claytonbe2362b2022-01-18 18:58:16 +000050 var color = in.color;
51 // Apply a circular particle alpha mask
52 color.a = color.a * max(1.0 - length(in.quad_pos), 0.0);
53 return color;
54}
55
56////////////////////////////////////////////////////////////////////////////////
57// Simulation Compute shader
58////////////////////////////////////////////////////////////////////////////////
59struct SimulationParams {
James Price3b671cb2022-03-28 14:31:22 +000060 deltaTime : f32,
61 seed : vec4<f32>,
Ben Claytonbe2362b2022-01-18 18:58:16 +000062};
63
64struct Particle {
James Price3b671cb2022-03-28 14:31:22 +000065 position : vec3<f32>,
66 lifetime : f32,
67 color : vec4<f32>,
68 velocity : vec3<f32>,
Ben Claytonbe2362b2022-01-18 18:58:16 +000069};
70
71struct Particles {
James Price3b671cb2022-03-28 14:31:22 +000072 particles : array<Particle>,
Ben Claytonbe2362b2022-01-18 18:58:16 +000073};
74
Ben Clayton01e4b6f2022-01-19 22:46:57 +000075@binding(0) @group(0) var<uniform> sim_params : SimulationParams;
76@binding(1) @group(0) var<storage, read_write> data : Particles;
77@binding(2) @group(0) var texture : texture_2d<f32>;
Ben Claytonbe2362b2022-01-18 18:58:16 +000078
dan sinclairb29892b2022-06-07 13:55:34 +000079@compute @workgroup_size(64)
Ben Clayton01e4b6f2022-01-19 22:46:57 +000080fn simulate(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
Ben Claytonbe2362b2022-01-18 18:58:16 +000081 rand_seed = (sim_params.seed.xy + vec2<f32>(GlobalInvocationID.xy)) * sim_params.seed.zw;
82
83 let idx = GlobalInvocationID.x;
84 var particle = data.particles[idx];
85
86 // Apply gravity
87 particle.velocity.z = particle.velocity.z - sim_params.deltaTime * 0.5;
88
89 // Basic velocity integration
90 particle.position = particle.position + sim_params.deltaTime * particle.velocity;
91
92 // Age each particle. Fade out before vanishing.
93 particle.lifetime = particle.lifetime - sim_params.deltaTime;
James Price03849322022-04-14 19:49:10 +000094 particle.color.a = smoothstep(0.0, 0.5, particle.lifetime);
Ben Claytonbe2362b2022-01-18 18:58:16 +000095
96 // If the lifetime has gone negative, then the particle is dead and should be
97 // respawned.
98 if (particle.lifetime < 0.0) {
99 // Use the probability map to find where the particle should be spawned.
100 // Starting with the 1x1 mip level.
Ben Clayton13f08902022-10-26 18:36:44 +0000101 var coord = vec2<u32>(0, 0);
Ben Claytonbe2362b2022-01-18 18:58:16 +0000102 for (var level = textureNumLevels(texture) - 1; level > 0; level = level - 1) {
103 // Load the probability value from the mip-level
104 // Generate a random number and using the probabilty values, pick the
105 // next texel in the next largest mip level:
106 //
107 // 0.0 probabilites.r probabilites.g probabilites.b 1.0
108 // | | | | |
109 // | TOP-LEFT | TOP-RIGHT | BOTTOM-LEFT | BOTTOM_RIGHT |
110 //
111 let probabilites = textureLoad(texture, coord, level);
112 let value = vec4<f32>(rand());
113 let mask = (value >= vec4<f32>(0.0, probabilites.xyz)) & (value < probabilites);
114 coord = coord * 2;
Ben Clayton13f08902022-10-26 18:36:44 +0000115 coord.x += select(0u, 1u, any(mask.yw)); // x y
116 coord.y += select(0u, 1u, any(mask.zw)); // z w
Ben Claytonbe2362b2022-01-18 18:58:16 +0000117 }
118 let uv = vec2<f32>(coord) / vec2<f32>(textureDimensions(texture));
119 particle.position = vec3<f32>((uv - 0.5) * 3.0 * vec2<f32>(1.0, -1.0), 0.0);
120 particle.color = textureLoad(texture, coord, 0);
121 particle.velocity.x = (rand() - 0.5) * 0.1;
122 particle.velocity.y = (rand() - 0.5) * 0.1;
123 particle.velocity.z = rand() * 0.3;
124 particle.lifetime = 0.5 + rand() * 2.0;
125 }
126
127 // Store the new particle value
128 data.particles[idx] = particle;
129}
130
131struct UBO {
James Price3b671cb2022-03-28 14:31:22 +0000132 width : u32,
Ben Claytonbe2362b2022-01-18 18:58:16 +0000133};
134
135struct Buffer {
James Price3b671cb2022-03-28 14:31:22 +0000136 weights : array<f32>,
Ben Claytonbe2362b2022-01-18 18:58:16 +0000137};
138
Ben Clayton01e4b6f2022-01-19 22:46:57 +0000139@binding(3) @group(0) var<uniform> ubo : UBO;
140@binding(4) @group(0) var<storage, read> buf_in : Buffer;
141@binding(5) @group(0) var<storage, read_write> buf_out : Buffer;
142@binding(6) @group(0) var tex_in : texture_2d<f32>;
143@binding(7) @group(0) var tex_out : texture_storage_2d<rgba8unorm, write>;
Ben Claytonbe2362b2022-01-18 18:58:16 +0000144
145////////////////////////////////////////////////////////////////////////////////
146// import_level
147//
148// Loads the alpha channel from a texel of the source image, and writes it to
149// the buf_out.weights.
150////////////////////////////////////////////////////////////////////////////////
dan sinclairb29892b2022-06-07 13:55:34 +0000151@compute @workgroup_size(64)
Ben Clayton01e4b6f2022-01-19 22:46:57 +0000152fn import_level(@builtin(global_invocation_id) coord : vec3<u32>) {
Ben Claytonbe2362b2022-01-18 18:58:16 +0000153 _ = &buf_in;
154 let offset = coord.x + coord.y * ubo.width;
155 buf_out.weights[offset] = textureLoad(tex_in, vec2<i32>(coord.xy), 0).w;
156}
157
158////////////////////////////////////////////////////////////////////////////////
159// export_level
160//
161// Loads 4 f32 weight values from buf_in.weights, and stores summed value into
162// buf_out.weights, along with the calculated 'probabilty' vec4 values into the
163// mip level of tex_out. See simulate() in particle.wgsl to understand the
164// probability logic.
165////////////////////////////////////////////////////////////////////////////////
dan sinclairb29892b2022-06-07 13:55:34 +0000166@compute @workgroup_size(64)
Ben Clayton01e4b6f2022-01-19 22:46:57 +0000167fn export_level(@builtin(global_invocation_id) coord : vec3<u32>) {
Ben Claytonbe2362b2022-01-18 18:58:16 +0000168 if (all(coord.xy < vec2<u32>(textureDimensions(tex_out)))) {
169 let dst_offset = coord.x + coord.y * ubo.width;
170 let src_offset = coord.x*2u + coord.y*2u * ubo.width;
171
172 let a = buf_in.weights[src_offset + 0u];
173 let b = buf_in.weights[src_offset + 1u];
174 let c = buf_in.weights[src_offset + 0u + ubo.width];
175 let d = buf_in.weights[src_offset + 1u + ubo.width];
176 let sum = dot(vec4<f32>(a, b, c, d), vec4<f32>(1.0));
177
178 buf_out.weights[dst_offset] = sum / 4.0;
179
180 let probabilities = vec4<f32>(a, a+b, a+b+c, sum) / max(sum, 0.0001);
181 textureStore(tex_out, vec2<i32>(coord.xy), probabilities);
182 }
183}