-
Notifications
You must be signed in to change notification settings - Fork 302
/
particle.wgsl
136 lines (117 loc) · 4.75 KB
/
particle.wgsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
////////////////////////////////////////////////////////////////////////////////
// Utilities
////////////////////////////////////////////////////////////////////////////////
var<private> rand_seed : vec2<f32>;
fn init_rand(invocation_id : u32, seed : vec4<f32>) {
rand_seed = seed.xz;
rand_seed = fract(rand_seed * cos(35.456+f32(invocation_id) * seed.yw));
rand_seed = fract(rand_seed * cos(41.235+f32(invocation_id) * seed.xw));
}
fn rand() -> f32 {
rand_seed.x = fract(cos(dot(rand_seed, vec2<f32>(23.14077926, 232.61690225))) * 136.8168);
rand_seed.y = fract(cos(dot(rand_seed, vec2<f32>(54.47856553, 345.84153136))) * 534.7645);
return rand_seed.y;
}
////////////////////////////////////////////////////////////////////////////////
// Vertex shader
////////////////////////////////////////////////////////////////////////////////
struct RenderParams {
modelViewProjectionMatrix : mat4x4<f32>,
right : vec3<f32>,
up : vec3<f32>
}
@binding(0) @group(0) var<uniform> render_params : RenderParams;
struct VertexInput {
@location(0) position : vec3<f32>,
@location(1) color : vec4<f32>,
@location(2) quad_pos : vec2<f32>, // -1..+1
}
struct VertexOutput {
@builtin(position) position : vec4<f32>,
@location(0) color : vec4<f32>,
@location(1) quad_pos : vec2<f32>, // -1..+1
}
@vertex
fn vs_main(in : VertexInput) -> VertexOutput {
var quad_pos = mat2x3<f32>(render_params.right, render_params.up) * in.quad_pos;
var position = in.position + quad_pos * 0.01;
var out : VertexOutput;
out.position = render_params.modelViewProjectionMatrix * vec4<f32>(position, 1.0);
out.color = in.color;
out.quad_pos = in.quad_pos;
return out;
}
////////////////////////////////////////////////////////////////////////////////
// Fragment shader
////////////////////////////////////////////////////////////////////////////////
@fragment
fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
var color = in.color;
// Apply a circular particle alpha mask
color.a = color.a * max(1.0 - length(in.quad_pos), 0.0);
return color;
}
////////////////////////////////////////////////////////////////////////////////
// Simulation Compute shader
////////////////////////////////////////////////////////////////////////////////
struct SimulationParams {
deltaTime : f32,
seed : vec4<f32>,
}
struct Particle {
position : vec3<f32>,
lifetime : f32,
color : vec4<f32>,
velocity : vec3<f32>,
}
struct Particles {
particles : array<Particle>,
}
@binding(0) @group(0) var<uniform> sim_params : SimulationParams;
@binding(1) @group(0) var<storage, read_write> data : Particles;
@binding(2) @group(0) var texture : texture_2d<f32>;
@compute @workgroup_size(64)
fn simulate(@builtin(global_invocation_id) global_invocation_id : vec3<u32>) {
let idx = global_invocation_id.x;
init_rand(idx, sim_params.seed);
var particle = data.particles[idx];
// Apply gravity
particle.velocity.z = particle.velocity.z - sim_params.deltaTime * 0.5;
// Basic velocity integration
particle.position = particle.position + sim_params.deltaTime * particle.velocity;
// Age each particle. Fade out before vanishing.
particle.lifetime = particle.lifetime - sim_params.deltaTime;
particle.color.a = smoothstep(0.0, 0.5, particle.lifetime);
// If the lifetime has gone negative, then the particle is dead and should be
// respawned.
if (particle.lifetime < 0.0) {
// Use the probability map to find where the particle should be spawned.
// Starting with the 1x1 mip level.
var coord : vec2<i32>;
for (var level = u32(textureNumLevels(texture) - 1); level > 0; level--) {
// Load the probability value from the mip-level
// Generate a random number and using the probabilty values, pick the
// next texel in the next largest mip level:
//
// 0.0 probabilites.r probabilites.g probabilites.b 1.0
// | | | | |
// | TOP-LEFT | TOP-RIGHT | BOTTOM-LEFT | BOTTOM_RIGHT |
//
let probabilites = textureLoad(texture, coord, level);
let value = vec4<f32>(rand());
let mask = (value >= vec4<f32>(0.0, probabilites.xyz)) & (value < probabilites);
coord = coord * 2;
coord.x = coord.x + select(0, 1, any(mask.yw)); // x y
coord.y = coord.y + select(0, 1, any(mask.zw)); // z w
}
let uv = vec2<f32>(coord) / vec2<f32>(textureDimensions(texture));
particle.position = vec3<f32>((uv - 0.5) * 3.0 * vec2<f32>(1.0, -1.0), 0.0);
particle.color = textureLoad(texture, coord, 0);
particle.velocity.x = (rand() - 0.5) * 0.1;
particle.velocity.y = (rand() - 0.5) * 0.1;
particle.velocity.z = rand() * 0.3;
particle.lifetime = 0.5 + rand() * 3.0;
}
// Store the new particle value
data.particles[idx] = particle;
}