Skip to main content

Marching Voxels

A simple algorithm for rendering volumetric data.

Following my Bachelor’s Thesis on 3D mesh voxelization, I continued exploring volumetric rendering methods. One such method is the marching cubes method, which consists of dividing the rendering workload into blocks of 8 voxels (a cube) that can be rendered independently. This allows for massive parallelization in the drawing process, drastically decreasing the rendering time of the voxel structure while maintaining the simplicity of only having to analyze a few voxels in each iteration. This distribution makes the function a perfect candidate for graphical hardware acceleration (GPU).

3D Perlin noise rendered using the marching cubes algorithm.

The Marching Cubes Algorithm

Marching Cubes is an algorithm that allows extracting surfaces from volumetric information, such as a scalar density field. From a threshold value (isovalue), the algorithm traverses a discrete version of the scalar field cell by cell, determining how the surface defined by the isovalue intersects that particular cell. After this, using a precomputed look-up table, the triangles that make up the geometry of said surface are generated locally.

The implementation of the algorithm was carried out in Unity, using a Compute Shader that takes advantage of the power of the GPU to parallelize the algorithm, allowing its use in real-time efficiently. The developed tool allows you to generate a density based on another Compute Shader that samples a mathematical model in a 3D grid, which will be drawn using the Marching Cubes algorithm given the parameters entered in this interface.

Final render

Main interface of the density generation tool.

The volumetric information to be rendered can be of many different natures: magnetic fields, physical objects, generations using SDF (Signed Distance Fields), fluids, etc. In general, this rendering mode is ideal for any entity that can be faithfully modeled using mathematical equations, and whose modeling or animation is complex, as usually happens with fluids. Ideally, of course, it should be able to be computed in real-time, since although it could potentially be precomputed and saved as a set of volumetric texture frames, we would move to having 4 dimensions, which would lead to occupying large amounts of memory.

Simplex Noise Density

Density generated using Simplex Noise. White dots represent values of 1, black dots represent values of 0.

Below I briefly detail the different configurable variables of the implementation I made in Unity.

GridSize

This variable allows us to configure the resolution on each axis of the density. The higher the resolution, the more exact the mesh generated from it will be. However, the rendering cost can increase drastically, since being a volume, the cost increases in the order O(N^3), both temporally and spatially.

Final render

Top, resolution of 8 per axis. Bottom, the resolution is 32 per axis.

SurfaceLevel

This value parameterized between 0 and 1, also called isovalue, allows adjusting at what level of the density the mesh is rendered. Points where the density value is lower than the SurfaceLevel will be inside the mesh; where it is higher, they will be outside the mesh. Potentially, the SurfaceLevel value could be varied throughout the volume, which could generate more complex effects without the need to modify the underlying mathematical model.

Final render

In the upper image, the isovalue is 0.066; in the lower one, 0.238.

SmoothRendering

By default, the tool generates the mesh with the independent triangles method, which generates a flat surface effect. In addition, the structure of independent triangles is substantially more expensive to store in memory. If the SmoothRendering option is activated, the generated mesh will have a single-group shared vertex structure, with the normal interpolated between the independent triangles that shared said vertex, which will cause the Unity rendering engine to interpolate the normal for each fragment, allowing a smoother visualization of the lighting.

Final render

The image shows a comparison between both types of structure.

CloseBorders

It offers the possibility of leaving the mesh that is on the border of the density open or closed, beyond which there is no information about its value at each point in space. Useful when you want to generate an effect that is seen from the outside.

Final render

Top, CloseBorders is activated. Bottom, the option is deactivated.

Below you can see the operation of the tool in video in the Unity engine itself in real-time.

Rendering of 3D Simplex Noise.

Rendering of 3D geometry.

In conclusion, this project allowed me to delve into volumetric rendering techniques and the use of the GPU as a general computing tool, facing real performance, memory, and three-dimensional data design problems. The implementation of Marching Cubes through compute shaders in Unity demonstrates how classic algorithms can be adapted to modern architectures to work in real-time, maintaining flexibility and visual quality. This work well summarizes my interest in computer graphics, systems based on mathematical models, and the development of technical tools oriented towards both efficiency and visual experimentation.