5
$\begingroup$

I'm trying to render a particle simulation. I want each particle to leave a trace. To achieve this I'm using a framebuffer and two textures. On each frame the algorithm goes like this:

  1. Render to a framebuffer previously rendered screen texture with opacity * 0.9;
  2. Render current particles at updated positions;
  3. Switch to main display and render current content of the framebuffer;

This works fine, and I get nice looking particle trails:

enter image description here

Problem

I want to allow users to pan and zoom canvas with these particles, similar to a map interface. This is achieved by applying a transformation matrix to each particle.

I want to keep particle's state, but I don't want the pan/zoom to leave a particle trace:

Bad

Here I'm dragging canvas up and down with mouse cursor. See how particles leave a trace?

enter image description here

Good

Here I'm also dragging the canvas up and down, but particles remain on their trajectory, without leaving a pan trace:

enter image description here

I'm wondering what is a proper way of achieving this? I got a basic solution working by applying a transformation to a previous frame's texture, so that it approximately matches what will be rendered in the current frame, but I there still a few artifacts:

  1. When zooming in, the updated texture "explodes":

enter image description here

I assume this is because I'm using fixed point size for a particle (regardless of the zoom level), but when we scale texture, we scale rasterized image, and thus all particles rendered on it also get bigger.

  1. When a particle enters the scene (crosses the border of visible area), it is also very likely to leave a trail:

enter image description here

I'm not sure why this one happens. My screen/back textures are the same size, I'm just translating back texture. I thought it might be related to clamp_to_edge setting, but I don't know how to prove or validate this. I've got a workaround by extending canvas beyond visible area (say by 2%) and in the fragment shader I check that if particle is closer than 2% to the border - I give it transparent color. This kind of "fixes" the problem, but I feel uncomfortable not knowing why it's happening.

  1. Finally, if transformation is very-very small (think kinetic movement), I also observe artifacts:

enter image description here

I'd appreciate if you could explain some of these artifacts, suggest possible solutions, or general practices used to solve this problem.

$\endgroup$
  • $\begingroup$ What do you expect to happen for #1? Your assumption is correct. If you're scaling the accumulation texture but not the particles, then it's going to get larger (or smaller) than the particle. What is the problem with #3? I don't see anything odd about it. $\endgroup$ – user1118321 Oct 20 '17 at 6:01
  • $\begingroup$ in #3 the original particle is not moving. So, when panning translation is applied, I expect both textures to have synchronized transformation, and no fading trail should appear. Yet you can see when translation is very small at the end - there is a fading trail around particle, as if transformations slightly misaligned $\endgroup$ – Anvaka Oct 20 '17 at 6:27
6
$\begingroup$

I made a visualization a little like yours years ago, and used a similar technique, because it's the natural approach to the problem. After I spent a few years writing drivers for modern GPUs, I wouldn't use this approach at all, because it creates a dependency from one frame to the next, which introduces pipeline stalls and/or false sharing.

Today, I'd keep the path of the particle as part of its state, and render the whole trail each frame, instead of reusing the old FBO. It's conceptually a little more difficult, and you have to be careful that the way you draw the past path is exactly the same each frame. For example, if you represent the path with some kind of cubic spline, you can't just move the final control point forward, because it'll change the whole curve slightly. A circular buffer of (x, y) positions would be much easier to get right.

What you get in exchange for this extra complexity is (a) reduced frame time, memory use, and power consumption (which might be important if you're on mobile), and (b) you won't have your current problem at all. Maybe the frame time advantage won't be as big in WebGL, because you'll end up doing extra work in Javascript, which is slow, but memory bandwidth is more of a bottleneck on most systems. Just make sure that the co-ordinates you store are in the space of your simulation, and transform them into screen co-ordinates when you draw them, just like you would with a 3D scene.

$\endgroup$
  • $\begingroup$ Thank you! I'm actually computing position of a particle on a GPU and encode it into texture. The formulas to find next particle position are quite involved, and I want to support 100k - 1,000k particles. If I'd have to recompute positions for last N steps, I'm worried that would impact frame rate a lot. $\endgroup$ – Anvaka Oct 20 '17 at 16:42
  • $\begingroup$ I was assuming you'd save the positions each frame rather than recomputing. An array of particle positions is much less state to pass around than a framebuffer. $\endgroup$ – Dan Hulme Oct 24 '17 at 8:18
  • $\begingroup$ Dan, but I don't think I'm passing a framebuffer. I just use two textures and constantly swapping them. Does this mean I'm passing a framebuffer? Also I tried to re-render last 10 -15 steps and take into account transforms - performance was not acceptable.. Maybe I'm doing something wrong $\endgroup$ – Anvaka Oct 26 '17 at 21:44
  • $\begingroup$ Dan, here is the project that I was working on: anvaka.github.io/fieldplay I can finally share it. The source code is here github.com/anvaka/fieldplay - this is vector field explorer/simulation. Maybe playing with it would give you better ideas of what I'm trying to do? If you don't have time for this - no worries! $\endgroup$ – Anvaka Nov 2 '17 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.