From 7469856a9e71c2c91fb9ac81519e9bd0ecf98f98 Mon Sep 17 00:00:00 2001 From: tiye Date: Mon, 6 May 2024 04:52:08 +0800 Subject: [PATCH] demo of halvorsen attractor --- src/apps/attractor-halvorsen.mts | 78 +++++++++++++ src/apps/attractor-halvorsen.wgsl | 178 ++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 src/apps/attractor-halvorsen.mts create mode 100644 src/apps/attractor-halvorsen.wgsl diff --git a/src/apps/attractor-halvorsen.mts b/src/apps/attractor-halvorsen.mts new file mode 100644 index 0000000..abf37ad --- /dev/null +++ b/src/apps/attractor-halvorsen.mts @@ -0,0 +1,78 @@ +import { createRenderer } from "../index.mjs"; +import attractorCompute from "./attractor-halvorsen.wgsl?raw"; +import { fiboGridN, rand_middle } from "../math.mjs"; + +export let loadRenderer = async (canvas: HTMLCanvasElement) => { + let seedSize = 2000000; + + let renderFrame = await createRenderer( + canvas, + { + seedSize, + seedData: makeSeed(seedSize, 0), + params: [ + 0.04, // deltaT + 600.0, // scale + 0.001, // width + 0.99, // opacity + ], + computeShader: attractorCompute, + }, + { + vertexCount: 1, + vertexData: [0, 1, 2, 3], + indexData: [0, 1, 2, 1, 2, 3], + vertexBufferLayout: vertexBufferLayout, + // topology: "line-list", + bgColor: [0.1, 0.0, 0.2, 1.0], + } + ); + + return renderFrame; +}; + +let randPoint: [number, number, number] = [0, 0, 0]; +let area = 1.0; + +function makeSeed(numParticles: number, scale: number): Float32Array { + const buf = new Float32Array(numParticles * 8); + + for (let i = 0; i < numParticles; ++i) { + if (i % 24 == 0) { + let p = fiboGridN(i, numParticles); + randPoint = p; + } + + let b = 8 * i; + buf[b + 0] = randPoint[0]; + buf[b + 1] = randPoint[1]; + buf[b + 2] = randPoint[2]; + buf[b + 3] = rand_middle(0.8); // ages + buf[b + 4] = randPoint[0]; + buf[b + 5] = randPoint[1]; + buf[b + 6] = randPoint[2]; + buf[b + 7] = 0; // distance + } + + return buf; +} + +let vertexBufferLayout: GPUVertexBufferLayout[] = [ + { + // instanced particles buffer + arrayStride: 8 * 4, + stepMode: "instance", + attributes: [ + { shaderLocation: 0, offset: 0, format: "float32x3" }, + { shaderLocation: 1, offset: 3 * 4, format: "float32" }, + { shaderLocation: 2, offset: 4 * 4, format: "float32x3" }, + { shaderLocation: 3, offset: 7 * 4, format: "float32" }, + ], + }, + { + // vertex buffer + arrayStride: 1 * 4, + stepMode: "vertex", + attributes: [{ shaderLocation: 4, offset: 0, format: "uint32" }], + }, +]; diff --git a/src/apps/attractor-halvorsen.wgsl b/src/apps/attractor-halvorsen.wgsl new file mode 100644 index 0000000..27f8f2c --- /dev/null +++ b/src/apps/attractor-halvorsen.wgsl @@ -0,0 +1,178 @@ + + +/// ok +fn iterate_fn(p: vec3f, dt: f32) -> LorenzResult { + let a = 1.4; + let b = 40.; + let c = 2.; + let d0 = 2.5; + + let x = p.x; + let y = p.y; + let z = p.z; + + let dx = -a * x - 4. * y - 4. * z - y * y; + let dy = -a * y - 4. * z - 4. * x - z * z; + let dz = -a * z - 4. * x - 4. * y - x * x; + + // let dx = a * (y - x); + // let dy = b * x - c * x * z; + // let dz = exp(x * y) - d0 * z; + + var d = vec3(dx, dy, dz) * dt * 10.; + // let dl = length(d); + // if (dl > 0.2) { + // d = d / dl * 0.2; + // } else if (dl < 0.01) { + // d = d / dl * 0.01; + // } + let next = p + d; + // if (length(next) > 100.0) { + // next = vec3(0.1); + // } + return LorenzResult( + next, + vec3(dx, dy, dz), + length(d) * 8.8 + ); +} + + +struct Particle { + pos: vec3, + ages: f32, + prev_pos: vec3, + distance: f32, +} + +struct Params { + delta_t: f32, + scale: f32, + width: f32, + opacity: f32, +} + +struct Particles { + particles: array, +} + +@group(0) @binding(1) var params: Params; +@group(1) @binding(0) var particles_a: Particles; +@group(1) @binding(1) var particles_b: Particles; + +const tau = 10f; +const rou = 28f; + +struct LorenzResult { + position: vec3f, + velocity: vec3f, + distance: f32, +} + + + +fn rand(n: f32) -> f32 { return fract(sin(n) * 43758.5453123); } + +// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp +@compute @workgroup_size(64) +fn main(@builtin(global_invocation_id) GlobalInvocationID: vec3) { + var index = GlobalInvocationID.x; + + var v_pos = particles_a.particles[index].pos; + // let dd = floor(f32(index) / 80.0); + + let inner_idx = f32(index % 24u); + let group_idx = floor(f32(index) / 24.0); + + if index % 24u != 0u { + let prev = index - 1u; + particles_b.particles[index].pos = particles_a.particles[prev].pos; + // particles_b.particles[index].ages = particles_a.particles[prev].ages; + particles_b.particles[index].ages = inner_idx; + particles_b.particles[index].prev_pos = particles_a.particles[prev].prev_pos; + // particles_b.particles[index].distance = particles_a.particles[prev].distance; + particles_b.particles[index].distance = group_idx; + return; + } + + // let ret = lorenz(v_pos, params.delta_t * 0.01 * (2. + 2. * rand(f32(index)))); + let ret = iterate_fn(v_pos, params.delta_t * 0.001 * (2. + 2. * rand(f32(index)))); + + // Write back + particles_b.particles[index].pos = ret.position; + particles_b.particles[index].ages = inner_idx; + particles_b.particles[index].prev_pos = v_pos; + // particles_b.particles[index].distance += ret.distance; + particles_b.particles[index].distance = group_idx; +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(4) color: vec4, +} + +@vertex +fn vert_main( + @location(0) position0: vec3, + @location(1) ages: f32, + @location(2) prev_pos0: vec3, + @location(3) travel: f32, + @location(4) idx: u32, +) -> VertexOutput { + let position = position0 * 8.; + let prev_pos = prev_pos0 * 8.; + var pos: vec3; + let v0 = position - prev_pos; + var prev_position = prev_pos; + let forward: vec3 = uniforms.forward; + let right = normalize(cross(v0, forward)); + + // let front = params.length; + var width = params.width * 8.; + + if ages < 0.01 { + // prev_position = position; + width = 0.0; + } + // TODO hack + if distance(position, prev_pos) > 12.2 { + width = 0.0; + } + + if idx == 0u { + pos = position + right * width; + // pos += vec3(1.,1.,1.) * 100.0; + } else if idx == 1u { + pos = position - right * width; + } else if idx == 2u { + pos = prev_position + right * width; + } else if idx == 3u { + pos = prev_position - right * width; + } else { + pos = position; + } + + var output: VertexOutput; + let p0 = vec4(pos * params.scale, 1.0); + + let p: vec3 = transform_perspective(p0.xyz).point_position; + let scale: f32 = 0.00002; + + output.position = vec4(p * scale, 1.0); + // let c3: vec3 = hsl(fract(travel/100.), 0.8, fract(0.9 - ages * 0.0002)); + // let c3: vec3 = hsl(0.24, 0.8, 0.7 + 0.3 * sin(travel * 0.2)); + // let c3 = hsl(0.24, 0.99, 0.99 - dim); + // let c3 = vec3(0.99, 0.94, 0.2) * (1. - ages * 0.01); + let c3: vec3 = hsl(fract(travel * 0.000008 + 0.0), 0.998, 0.5 - ages * 0.002); + output.color = vec4(c3, params.opacity * (1.2 - ages * 0.04)); + return output; +} + +@fragment +fn frag_main(@location(4) color: vec4) -> @location(0) vec4 { + return color; + // return vec4(0.7, 0.7, 1., 1.0); +} + +#import protea::perspective +#import protea::colors