From a91af7ccb0d6e52c337854229c646751c2c186b4 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Sat, 1 Feb 2020 00:15:45 -0500 Subject: [PATCH] Implement compute-shader simulation of particles! This uses a workaround for the previously mentioned shader glitch in order to use the compute shaders successfully. Bjorn notes that reading from an SSBO makes rendering start working properly. In order to *hopefully* make this cheaper, I add a variable to the buffer that's intended for only reading like this, which ends up being sufficient to fix the behavior of the whole buffer. The discussion comment with the workaround: https://github.com/bjornbytes/lovr/issues/211#issuecomment-580985010 > Reading data out of the buffer (print(block:read('value')[1])) after > the compute also fixes it, I suspect maybe because it maps the buffer. For simulation, this updates the positions with the velocity, and applies uniform quadratic drag to the particles. --- main.lua | 25 +++++++++++++++++-------- shaders/particles.comp | 13 +++++++++---- shaders/particles.frag | 5 ++--- shaders/particles.vert | 9 ++++++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/main.lua b/main.lua index 9764877..b931f2f 100644 --- a/main.lua +++ b/main.lua @@ -1,6 +1,6 @@ local mat4 = lovr.math.mat4 -local nParticles = 2 ^ 16 +local nParticles = 2 ^ 14 local batchSize = 128 local batches = nParticles / batchSize @@ -12,12 +12,17 @@ local compute = {} function lovr.load() features = lovr.graphics.getFeatures() - shaders.particles = lovr.graphics.newShader("shaders/particles.vert", "shaders/particles.frag") blocks.particles = lovr.graphics.newShaderBlock("compute", { + -- Reading a value seems to work around whatever the rendering glitch + -- was. So we include this flag so we only have to ask for a single + -- value, instead of having to copy a large array. Still, annoying :( + -- For reference, see issue comment at: + -- https://github.com/bjornbytes/lovr/issues/211#issuecomment-580985010 + reloadFlag = "float", particles = {"mat4", nParticles}, - }, { readable = true, writable = true }) + }, { readable = true }) shaders.particles:sendBlock('particleData', blocks.particles) shaders.particles:send("batchSize", batchSize) @@ -25,16 +30,20 @@ function lovr.load() compute.particles = lovr.graphics.newComputeShader("shaders/particles.comp") compute.particles:sendBlock('particleData', blocks.particles) - compute.particles:send("dt", 0.0) + compute.particles:send("dt", 0.01) - local initial = initialParticleData(nParticles, range(-2, 2), range(0, 4), range(-2, 2), range(0.005, 0.01)) + local xr, yr, zr = range(-1, 1), range(1, 3), range(-2, 0) + local sr, vr = range(0.005, 0.01), range(-0.05, 0.05) + local initial = initialParticleData(nParticles, xr, yr, zr, sr, vr) blocks.particles:send("particles", initial) end function lovr.draw() lovr.graphics.clear() local x, y, z = lovr.headset.getPose("head") - -- lovr.graphics.compute(compute.particles, batches) + lovr.graphics.compute(compute.particles, batches) + -- Read the buffer to force SSBO synch of some sort. + blocks.particles:read("reloadFlag") shaders.particles:send("headPos", {x, y, z}) lovr.graphics.setColor(0.6, 0.1, 0) @@ -81,13 +90,13 @@ function range(min, max) end end -function initialParticleData(size, xRange, yRange, zRange, sRange) +function initialParticleData(size, xRange, yRange, zRange, sRange, vRange) local particles = {} local insert = table.insert for i = 1, size do insert(particles, { xRange(), yRange(), zRange(), sRange(), - xRange(), yRange(), zRange(), 1, + vRange(), vRange(), vRange(), 1, 0, 0, 0, 0, 0, 0, 0, 0, }) diff --git a/shaders/particles.comp b/shaders/particles.comp index f2553ae..6e136b4 100644 --- a/shaders/particles.comp +++ b/shaders/particles.comp @@ -1,15 +1,20 @@ uniform float dt; + struct particle_t { vec4 positionAndSize; vec4 velocity; vec4 padding2; // for LÖVR's limited ShaderBlock type :( vec4 padding3; // for LÖVR's limited ShaderBlock type :( }; -layout(std430) buffer particleData { particle_t particles[]; }; +layout(std430) buffer particleData { + float reloadFlag; + particle_t particles[]; +}; layout(local_size_x = 128) in; void compute() { - uint particleId = gl_GlobalInvocationID.x; - particles[particleId].positionAndSize.xyz += - particles[particleId].velocity.xyz * dt; + uint id = gl_GlobalInvocationID.x; + vec3 vel = particles[id].velocity.xyz; + particles[id].positionAndSize.xyz += vel * dt; + particles[id].velocity.xyz -= vel * length(vel) * dt; } diff --git a/shaders/particles.frag b/shaders/particles.frag index 5c0f85d..1564a4e 100644 --- a/shaders/particles.frag +++ b/shaders/particles.frag @@ -1,8 +1,7 @@ in vec2 uv; -in vec4 particleColor; vec4 color(vec4 graphicsColor, sampler2D image, vec2 _uv) { float alpha = 1.0 - (distance(uv, vec2(0.5)) * 2.0); - graphicsColor.a *= alpha; - return graphicsColor * particleColor; + graphicsColor.a *= sqrt(clamp(alpha, 0.0, 1.0)); + return graphicsColor; } diff --git a/shaders/particles.vert b/shaders/particles.vert index b40c094..533203b 100644 --- a/shaders/particles.vert +++ b/shaders/particles.vert @@ -1,22 +1,25 @@ in vec2 vertPosition; out vec2 uv; -out vec4 particleColor; + uniform vec3 headPos; uniform int batchSize; + struct particle_t { vec4 positionAndSize; vec4 velocity; vec4 padding2; // for LÖVR's limited ShaderBlock type :( vec4 padding3; // for LÖVR's limited ShaderBlock type :( }; -layout(std430) buffer particleData { particle_t particles[]; }; +layout(std430) buffer particleData { + float reloadFlag; + particle_t particles[]; +}; vec4 position(mat4 projection, mat4 transform, vec4 vertex) { int index = gl_VertexID / 4 + gl_InstanceID * batchSize; particle_t particle = particles[index]; vec3 pos = particle.positionAndSize.xyz; float size = particle.positionAndSize.w; - particleColor = particle.velocity; uv = vertPosition * 0.5 + 0.5; vec3 forward = normalize(pos - headPos); -- 2.47.0