Creating a voxel rain effect with MagicaVoxel and VoxBox

MagicaVoxel is a powerful voxel editing and rendering program but creating large animations by hand is a lot of work. In my previous post I explained how Python and VoxBox could be used to generate water and add it into an existing MagicaVoxel scene. In this post I provide another example by using a similar process to create rain. The result can be seen below:

Implementation

The implementation of this effect is relatively straight-forward. Given a static MagicaVoxel scene we perform the following steps:

  1. Create a boolean ‘rain volume’ with most of the elements set to False, but a few set to True to represent the initial position of the rain drops.
  2. Optionally stretch the raindrops to give them an elongated appearance.
  3. For each output frame:
    1. Copy the input scene into the current output frame.
    2. For each column of the current output frame:
      1. Copy the corresponding column of the rain volume, starting at the top.
      2. Stop copying if the current output frame voxel is not empty (so the rain is blocked by objects in the scene).
    3. Roll the contents of the rain volume along the vertical axis to move the rain ready for the next frame.

Rain drop generation

Surprisingly, the most challenging aspect of this process proved to be the initial generation of the rain volume. The obvious approach was to start with an empty volume and then use the Python rand() function to set a certain number of the voxels. However, it was possible to see some structure in the resulting rain which made it too easy to notice that the animation was repeating after only a short number of frames.

To fix this I needed to constrain the initial random points to be spread evenly throughout the rain volume. This page contains some useful information on how to do this with Poission-Disc sampling, but although the ‘best-candidate algorithm‘ does not seem particular complicated it was none-the-less a distraction from the task at hand, and I didn’t find a built-in implementation in NumPy/SciPy.

Eventually I settled on performing thresholding on a 3D Blue Noise texture to generate the initial points. I’m not exactly clear on the mathematical link between Poisson-Disc sampling and Blue Noise, but I can see that the previously-mentioned ‘best-candidate algorithm’ has some conceptual similarities to the ‘void-and-cluster method‘, so I suspect a link does exist. At any rate, using a precomputed texture was much faster than trying to generate the values on-the-fly.

Result

Final touches included adding splashes where the rain hit the ground, and then the final render was set up with the same settings as in my previous post.

As before, the source code is available on GitHub. Feel free to play with it and let me know if you come up with anything cool!

Leave a Reply

Your email address will not be published. Required fields are marked *