Marching cubes and making simply cute planets.

Using the Marching-Cubes graphical algorithm to render infinite and customizable planets with ease.

A earth like planet

While developing my own 3D fishing game, I found myself in a boat as a character with flat land not being ambitious enough. At this time I was working in height maps, but height maps are limited in scope, and don't give you much breathing room when it comes to a infinite landscape. So this how I learned about marching cubes, and how simple it is to implement them and make some of the darn cutest planets.

What the heck is a marching cube?

Marching Cubes is a fancy-sounding name for an algorithm that turns 3D data into 3D shapes. Here’s the technical definition:

Marching Cubes is an algorithm used to create a polygonal mesh from a 3D scalar field (a 3D grid of values). It extracts a surface—also called an isosurface—by checking the values at the corners of each cube (voxel) and figuring out how to connect them into triangles.

Okay, but what does that actually mean?

In simpler terms, marching cubes helps generate voxel-based meshes by deciding where the surface of something should be. It looks at:

  • The lowest point
  • The highest point
  • The surface threshold (basically where we want the shape to show up)

Here's how it works in practice: you create a function that returns a single value based on an input position (x, y, z). This function forms what’s called a "density map." The marching cubes algorithm then runs through this map—if a value is above the surface level, it’s considered "solid" and part of the shape; if it’s below, it’s ignored. This blog is all about the code, so I define this noise function something like this,


                // Convert local chunk coordinates to world coordinates
                    int worldX = chunkCoordinates.x * size.x + x;
                    int worldY = chunkCoordinates.y * size.y + y;
                    int worldZ = chunkCoordinates.z * size.z + z;

                    // Distance from center of the planet
                    Vector3 worldPos = new Vector3(worldX, worldY, worldZ);
                    float dist = Vector3.Distance(worldPos, PlanetCenter);

                    // Give the planet some roughness.
                    float sphericalNoise = Perlin.Fbm(worldX * 0.06f, worldY * 0.06f, worldZ * 0.06f, 5);

                    float sampleFreq = Options.Frequency * Options.NoiseScale;

                    // FBM is Fractal Brownian Motion
                    float noiseValue = Perlin.Fbm(
                        worldX * sampleFreq,
                        worldY * sampleFreq,
                        worldZ * sampleFreq,
                        Options.Octaves) * Options.Amplitude;

                    float bumpyRadius = PlanetRadius
                        + (sphericalNoise - 0.5f) * 5f
                        + (noiseValue) * Options.NoiseMultiplier;

                    densityMap[x, y, z] = (bumpyRadius - dist) * 0.05f;
            

This creates what we call the 'densityMap'. There is quite a few variables that go into the noise generation. The algorithm then builds triangles around these points to create a 3D mesh. That’s how you go from a blob of numbers to an interesting.

If you found this interesting and want a more in-depth view, this Youtube creator made a fantastic 5m compilation.

How I handled chunk logic