var rand_seed : vec2; fn rand() -> f32 { rand_seed.x = fract((cos(dot(rand_seed, vec2(23.140779495, 232.616897583))) * 136.816802979)); rand_seed.y = fract((cos(dot(rand_seed, vec2(54.478565216, 345.841522217))) * 534.764526367)); return rand_seed.y; } struct RenderParams { modelViewProjectionMatrix : mat4x4; right : vec3; up : vec3; } @binding(0) @group(0) var render_params : RenderParams; struct VertexInput { @location(0) position : vec3; @location(1) color : vec4; @location(2) quad_pos : vec2; } struct VertexOutput { @builtin(position) position : vec4; @location(0) color : vec4; @location(1) quad_pos : vec2; } @stage(vertex) fn vs_main(in : VertexInput) -> VertexOutput { var quad_pos = (mat2x3(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(position, 1.0)); out.color = in.color; out.quad_pos = in.quad_pos; return out; } @stage(fragment) fn fs_main(in : VertexOutput) -> @location(0) vec4 { var color = in.color; color.a = (color.a * max((1.0 - length(in.quad_pos)), 0.0)); return color; } struct SimulationParams { deltaTime : f32; seed : vec4; } struct Particle { position : vec3; lifetime : f32; color : vec4; velocity : vec3; } struct Particles { particles : array; } @binding(0) @group(0) var sim_params : SimulationParams; @binding(1) @group(0) var data : Particles; @binding(2) @group(0) var texture : texture_2d; @stage(compute) @workgroup_size(64) fn simulate(@builtin(global_invocation_id) GlobalInvocationID : vec3) { rand_seed = ((sim_params.seed.xy + vec2(GlobalInvocationID.xy)) * sim_params.seed.zw); let idx = GlobalInvocationID.x; var particle = data.particles[idx]; particle.velocity.z = (particle.velocity.z - (sim_params.deltaTime * 0.5)); particle.position = (particle.position + (sim_params.deltaTime * particle.velocity)); particle.lifetime = (particle.lifetime - sim_params.deltaTime); particle.color.a = smoothStep(0.0, 0.5, particle.lifetime); if ((particle.lifetime < 0.0)) { var coord = vec2(0, 0); for(var level = (textureNumLevels(texture) - 1); (level > 0); level = (level - 1)) { let probabilites = textureLoad(texture, coord, level); let value = vec4(rand()); let mask = ((value >= vec4(0.0, probabilites.xyz)) & (value < probabilites)); coord = (coord * 2); coord.x = (coord.x + select(0, 1, any(mask.yw))); coord.y = (coord.y + select(0, 1, any(mask.zw))); } let uv = (vec2(coord) / vec2(textureDimensions(texture))); particle.position = vec3((((uv - 0.5) * 3.0) * vec2(1.0, -1.0)), 0.0); particle.color = textureLoad(texture, coord, 0); particle.velocity.x = ((rand() - 0.5) * 0.100000001); particle.velocity.y = ((rand() - 0.5) * 0.100000001); particle.velocity.z = (rand() * 0.300000012); particle.lifetime = (0.5 + (rand() * 2.0)); } data.particles[idx] = particle; } struct UBO { width : u32; } struct Buffer { weights : array; } @binding(3) @group(0) var ubo : UBO; @binding(4) @group(0) var buf_in : Buffer; @binding(5) @group(0) var buf_out : Buffer; @binding(6) @group(0) var tex_in : texture_2d; @binding(7) @group(0) var tex_out : texture_storage_2d; @stage(compute) @workgroup_size(64) fn import_level(@builtin(global_invocation_id) coord : vec3) { _ = &(buf_in); let offset = (coord.x + (coord.y * ubo.width)); buf_out.weights[offset] = textureLoad(tex_in, vec2(coord.xy), 0).w; } @stage(compute) @workgroup_size(64) fn export_level(@builtin(global_invocation_id) coord : vec3) { if (all((coord.xy < vec2(textureDimensions(tex_out))))) { let dst_offset = (coord.x + (coord.y * ubo.width)); let src_offset = ((coord.x * 2u) + ((coord.y * 2u) * ubo.width)); let a = buf_in.weights[(src_offset + 0u)]; let b = buf_in.weights[(src_offset + 1u)]; let c = buf_in.weights[((src_offset + 0u) + ubo.width)]; let d = buf_in.weights[((src_offset + 1u) + ubo.width)]; let sum = dot(vec4(a, b, c, d), vec4(1.0)); buf_out.weights[dst_offset] = (sum / 4.0); let probabilities = (vec4(a, (a + b), ((a + b) + c), sum) / max(sum, 0.0001)); textureStore(tex_out, vec2(coord.xy), probabilities); } }