Now that Particlescape has been released on the app store, I wanted to use this space to talk about the app in more detail. There are a number of optimizations within Particlescape to allow it to perform at as high a framerate as possible and to give the illusion of as dense and detailed a particle field as possible. First, I made the design choice fairly early on to scale the rendered size of each particle inversely proportional to its local density. This means that in high-density regions, particles are rendered smaller and more sharply, but in low-density regions, particles are bigger and look more diffuse and are in fact more transparent. This technique brings out detail in the high-density regions while making the low-density regions look more uniform. I think the effect looks better than rendering all the particles at the same size. When actually playing with Particlescape, your eyes should be drawn to the high-density regions where interesting things are happening anyway, and the fact that the low-density regions are more diffuse gives the illusion that more is going on than actually is. That’s the intention anyway.
Because of this effect, each particle needs to know about its local density. I calculate the density on a grid, but since I know the velocity of each particle, I can keep track of how far the particle’s moved since its last “registration” on the grid. For example, if a particle has zero velocity, there’s no possible way for it to have affected the density grid, so I just accumulate particle movements, and when a particle’s cumulative movement surpasses a grid cell width, the new bin for that particle is calculated, and the density grid can be adjusted accordingly. This method isn’t 100% accurate but it doesn’t need to be, and the optimization is helpful.
For small numbers of particles, the gravity calculation is brute-force, using Newton’s inverse-square law. But when the number of particles multiplied by the number of gravity sinks surpasses a critical value (currently 60000), I transition the gravity calculation algorithm to something more optimized, but less accurate. I calculate the gravity due to the sinks at test points across the field (actually defined on a grid), and then linearly interpolate the force for each particle. This avoids doing N_touches * N_particles inverse-square calculations, which is more expensive.
There’s also an optimization for which, if a given particle is known to have a small acceleration, then it can cache its acceleration for a few ticks and avoid calculating the inverse-square law.
So these are some of the optimizations that I implemented. The goal was to create a field with as high resolution as possible at as high a framerate as possible.