If you don’t know what Diffusion-Limited Aggregation (aka DLA) is, see this post of mine.
Previously, my best 3D DLA movies were these two;
I had two main issues with those movies. Firstly, I used software based OpenGL which does not support shadows or any advanced lighting techniques. Secondly, I was launching the moving particles on the surface of a sphere that was biased to the poles, which meant the resulting growth tended to grow more in the “up” and “down” directions rather than a nice spherical shape. This is especially noticable in the first movie above that grows more in the up direction than the other directions.
Now that Visions of Chaos is 64-bit I wanted to push the limits of the DLA animations even further. In the past the 32-bit executable limited me to around 3GB of usable memory which restricted the size of the arrays that I could grow DLA structures within to around 750x750x750. With a 64-bit executable Visions of Chaos can now use as much memory as the PC has available.
Bugs in old code
I have been experimenting with 2D and 3D DLA for many years now and my existing code reflected that. When I started expanding the grid size and particle counts I was getting strange crashes/hangs/poor behavior. As any programmer knows, you think “this will be an easy fix”. Well, 3 or 4 days later after trying to fix these simple bugs and rendering lengthy test runs I was ready to chuck my PC out the window. Having to wait a few hours to see if a fix works or not really tests the patience. In the end I bit the bullet and woke up one morning and rewrote the code from scratch. It took a few hours and I now have a much more stable and faster version. I also added support for Mitsuba so I get nicely shaded images. Back on track.
With the extra memory available from a 64-bit Visions of Chaos executable and being able to use Mitsuba to render each frame it was time to let my PC churn away for a few days rendering DLA frames. The few days expanded into a few weeks as I kept tweaking settings and code and started to render the DLA movie parts again from scratch. But finally, the following movie was complete.
I only have 32 GB memory in my main PC so those sample movies run out of RAM when around 10 million particles are reached. This is double the maximum particles I achieved in the past. I need to look at maxing out my memory to 128 GB so I can create even larger DLA structures.
Something to observe in that movie is how once the particle count gets beyond around one million the overall structure remains the same as it continues to grow. This is a great example of the self-similarity properties of fractals. With more memory and more particles the overall structure would not suddenly change beyond 10 million particles. The individual spheres would shrink into sub pixel sizes, but the overall growing shapes would remain self-similar (as render times continued to increased per frame). This is also noticeable in most of the sample images in this post.
RenderMan Blobby Implicit Surfaces
Using RenderMan as a rendering engine allows the use of blobby implicit surfaces aka metaballs. The metaball process merges the individual spheres into a blobby surface and gives the following result.
I didn’t let these examples run for as long as the previous example movie. This is because they were mainly as a quick example of blobby implicit surface rendering. Also because they were reaching the levels of detail that further steps don’t add any new structure shapes (due to the self similarity of DLA). And mainly because RenderMan was starting to stretch my patience taking over 6 minutes to render each frame.
Some DLA coding tips for programmers
If you are not programming 3D DLA then this next bit will be of little use to you and you can feel free to skip it.
When launching particles into the DLA space use random points on a sphere surrounding the current growth. If you use random points on a cube surrounding the growth it will be biased to grow along the axiis. Using a sphere helps maintain a more spherical growth. Set the radius of the sphere slightly larger than the furthest distance the dla structure has grown. ie if the furthest particle is 10 units of length from the origin, then set the sphere size to 13. Also make sure you use an even distribution of points on the sphere surface. My original code was biased to the poles and this is why the first sample movie above grows towards the up/north direction more than evenly in all directions. Some quick code to evenly distribute random points on a sphere is
theta:=2*pi*random; phi:=arccos(1-2*random); px:=round(sin(phi)*cos(theta)*launchradius)+centerx; py:=round(sin(phi)*sin(theta)*launchradius)+centery; pz:=round(cos(phi)*launchradius)+centerz;
The usual DLA method is to wait until a moving particle goes “off screen” before a new particle is launched. For 3D, off screen is the array dimensions. Waiting for particles to move off the existing DLA grid can really slow down the process (especially for large sized grids). Rather than wait for the edge of the grid, use a few grid units distance beyond the launch sphere radius. So if the launch sphere is radius 13 then moving particles get discarded if they travel more than 16 distance from the origin.
Calculating distances involves costly sqrt calculations. If you are doing a sqrt each time the particle is moving they quickly add up and slow down the simulation. To speed up distance calcs I fill an array once at the start of the simulation that contains the distance from the origin (grid center) to each array location. This makes it much quicker when you want to know how far a moving particle is from the origin. All it takes is a single array lookup rather than a sqrt distance calculation.
Another thing you want to know for moving particles is how many neighbor particles is it touching. For example if the current settings make a particle stick to 3 or more existing neighbors then you usually do a quick loop of neighbor cells adding them up. Again, doing this every time the moving particles move adds up and slows everything down. I use another array that holds the neighbor count for each grid location. When a new particle attaches to the existing DLA structure, you add 1 to the surrounding cells in the neighbors array. Much faster.
If you are rendering a very dense DLA then there will be a lot of particles within the middle that remain hidden. Doing a quick check to see if a particle is surrounded (ie using the above neighbors array means if neighbors=26) means it can be skipped and not sent to Mitsuba for rendering. On the densest DLA structures this cut down the number of spheres passed to OpenGL and/or Mitsuba to only 7% of the total spheres. A huge speed up in terms of time per frame.
You need to auto-increase the hits per update each frame. ie if you set the display to update every time 10 particles get stuck to the growing structure it will look fine at the start, but once the structure starts growing you will have a long wait to get to a million particles and beyond. I used a simple formula of increasing the stuck particles per frame as the number of total stuck particles div 40. Once you get to 100000 particles, keep the update at 25,000 particles per frame. This allows a nice smooth increase in stuck particles and gets you to the more interesting million+ range quicker.
Using the above tricks you will find the particle movements and sticking part of the code takes seconds rather than minutes per frame. The main slowdown is now in the display code.