Terrain Generators

intermediate concepts

Voxel Play 3 · World Architecture

Each World must have a Terrain Generator attached to the terrain generator field:

Voxel play terrain generators 0

The terrain generator component is responsible for providing basic data about any world position like ground altitude, moisture and biome type.

The demo Earth world provided with Voxel Play contains a terrain generator that you can modify, duplicate or reuse:

Voxel play terrain generators 1

You can also create a new terrain generator right-clicking on the Project Panel and selecting Create -> VoxelPlay -> Terrain Generators -> Default Terrain Generator:

Voxel play terrain generators 2

This option will create an terrain generator based on the Default Terrain Generator algorithm provided by Voxel Play (see “Coding your own terrain generator” for instructions on how to create your own methods).

Select the Terrain Generator to see its properties or click “Expand/Collapse Generator Settings in the Voxel Play Environment inspector to see its properties:

Voxel play terrain generators 3

Terrain Graph Editor

The Terrain Graph Editor provides a visual, node-based interface to design terrain generation algorithms. You can create, connect and preview terrain operations on a visual graph canvas.

To open the Graph Editor, select the Terrain Generator asset and click Open in Graph Editor in the Inspector:

Terrain Settings inspector with Open in Graph Editor button

The Inspector also exposes the main terrain parameters:

  • Max Height: maximum terrain altitude in voxels (default 255).
  • Min Height: minimum terrain altitude. Negative values allow underground depth.
  • Add Water / Water Level: enables water generation and sets the water level in voxels.
  • Sea Depth Multiplier: scales depth below sea level for deeper or shallower oceans.
  • Beach Width: controls the shore voxel area around the water line.
  • Water Voxel / Shore Voxel / Bedrock Voxel: voxel definitions used for water, beaches and the bottom layer.
  • Add Ore: enables ore generation underground based on biome settings.
  • Enable Half Step Surface: uses microvoxels for smoother terrain surfaces.

Graph Editor Interface

Terrain Graph Editor window

The Graph Editor window is divided into the following areas:

  • Toolbar: buttons for creating new graphs (New), saving (Save or Ctrl+S), auto-arranging nodes (Auto Layout), fitting the view (Fit View), toggling the minimap and preview panel, and reloading.
  • Canvas: the main area where nodes are placed and connected. Scroll to zoom, drag to pan.
  • MiniMap: a small overview in the top-left corner showing the full graph layout.
  • Preview panel: shown in the bottom-right corner, it renders a real-time heightmap or biome preview of the terrain produced by the current graph. It displays altitude range, coverage area and water level. Click on the preview to inspect values at a specific world position.

Working with Nodes

Right-click on the canvas to open the Create Node menu. Nodes are organized into four categories, each with a distinct color:

CategoryColorDescription
SamplersGreenGenerate initial terrain values from noise textures, fractal noise, Unity Terrain data, constants or random values.
MathOrangeTransform a single value: shift, invert, scale, exponent, clamp, flatten/raise, island falloff.
BlendingPurpleCombine two input values using additive or multiplicative blending with custom weights.
FiltersYellowConditional operations: threshold, fill, test, copy and beach mask.

Each node shows its input ports on the left and output port on the right. To connect nodes, drag from an output port to an input port. Values flow through connections and are evaluated in topological order.

The graph has two special output nodes:

  • Altitude Output: the final terrain height value (normalized 0 to 1).
  • Moisture Output: an optional secondary value used together with altitude for biome selection.

Click on a node title to collapse or expand its parameters. Many numeric fields support switching between Normalized (0..1) and Meters units using the dropdown next to the field.

Tip: Use the Preview panel to see how your changes affect the terrain in real time. Switch between Heightmap and Biome preview modes to visualize altitude and biome distribution respectively.

Available Node Operations

Full Reference: For a comprehensive reference of all 30 node types with parameters, formulas, and port descriptions, see the dedicated Terrain Graph Editor page.

The following operations are available as nodes in the Graph Editor. Each node produces a float output value that can be connected to other nodes:

Samplers (green nodes - generate initial values):

  • Sample Height Map Texture: reads a value from a 2D noise texture. Configure frequency (sampling scale) and output range (min/max) to remap values.
  • Sample Ridge Noise From Texture: similar to Sample Height Map Texture but applies a ridge formula: 2 * (0.5 - abs(0.5 - value)). Produces acute reliefs, useful for rivers (when inverted) or pointy mountains.
  • Sample Heightmap Fractal: generates Perlin fractal noise with configurable octaves, persistence and lacunarity for detailed, natural-looking terrain.
  • Sample Heightmap from Unity Terrain: reads height data from an existing Unity Terrain asset in the scene.
  • Constant: outputs a fixed constant value.
  • Random: produces a deterministic pseudo-random value (0-1) based on world X/Z position.

Math (orange nodes - transform a single value):

  • Shift: adds a constant to the input value. Supports both normalized and meter units.
  • Invert: outputs 1 - input.
  • Add and Multiply: computes (input + Add) * Multiply.
  • Multiply And Add: computes (input * Multiply) + Add.
  • Exponential: raises the input to a power. Useful to accentuate valleys and mountain shapes.
  • Flatten or Raise: if the input is above a threshold, multiplies it by a factor. Values below the threshold pass through unchanged.
  • Clamp: constrains the value to a given min/max range.
  • Island: reduces the height based on distance from the world origin, creating island-like falloff.

Blending (purple nodes - combine two values):

  • Blend Additive: combines two inputs with custom weights: A * WeightA + B * WeightB.
  • Blend Multiply: multiplies two input values together.

Filters (yellow nodes - conditional operations):

  • Threshold: if the input is greater than or equal to a threshold, outputs that value plus an optional shift. Otherwise outputs a fallback constant.
  • Fill: replaces any value within a given range with a constant. Values outside the range pass through.
  • Test: outputs 1 if the input falls inside a given range, otherwise outputs 0.
  • Copy: explicitly references and passes through a previous node's output.
  • Beach Mask: suppresses beach/shore voxels in areas where the mask input exceeds a threshold.

Feel free to experiment with nodes and see the effect on the world. We recommend enabling the Render In Editor toggle so you can test the changes inside the Scene View.

About Biomes

The terrain generator is responsible for producing an altitude and moisture (humidity) value for any world position. Using this altitude and moisture, Voxel Play looks up the matching biome and picks the appropriate voxel definition for the surface and the underground voxels.

Check the biome definitions page for more details about how to define biomes based on altitude and moisture ranges.

Complete world & terrain generation tutorial

Coding Your Own Terrain Generator

The default terrain generator covers most use cases with its graph-based pipeline, but sometimes you need full control - procedural planets, custom noise algorithms, importing external heightmap data, or game-specific terrain logic. In those cases, you can write your own terrain generator by subclassing VoxelPlayTerrainGenerator.

Thread safety: All terrain generator methods run on the main thread, so you can safely use the Unity API (e.g. Mathf, Vector3, textures) inside any method.

How generation works: When a chunk needs terrain, Voxel Play creates an empty chunk (16x16x16 voxels) and calls your PaintChunk method. Your job is to fill the chunk's voxels array with the appropriate voxel types. Return true if you placed any voxels, or false if the chunk is empty (e.g. it's entirely above the terrain surface).

API Reference

abstract bool PaintChunk(VoxelChunk chunk)

The only required method. Called once per chunk when it first needs terrain. Fill chunk.voxels[] with your terrain data. Return true if the chunk contains any voxels, false if it's empty.

The chunk's world position is available via chunk.position (a Vector3d). You'll typically compute the chunk's vertical range, determine which voxels should be solid, water, or air, and write them using the fast voxel helpers listed below.

virtual void Init()

Called once when the world initializes. Use it to set up noise generators, load textures, cache voxel definitions, or pre-compute lookup tables. This is the right place for any one-time setup that your PaintChunk method will rely on.

virtual void GetHeightAndMoisture(double x, double z, out float altitude, out float moisture)

Called by Voxel Play to determine biome selection at a given world position. Override this if your terrain generator computes its own heightmap - return the normalized altitude (0..1 relative to max height) and moisture values so VP can pick the correct biome. If you don't override it, biome selection is disabled.

virtual void GetTerrainVoxelDefinitions(List<VoxelDefinition> voxelDefinitions)

Add all voxel definitions your generator uses to the provided list. This helps Voxel Play register them properly in the editor and ensures they're available during chunk generation. If not overridden, a warning is logged - it's recommended to implement this even if your generator only uses a few voxel types.

virtual bool GetTerrainBounds(out Vector3 center, out Vector3 size)

Override this method to report the terrain area bounds. Voxel Play uses this information to constrain features like Start On Flat spawning to valid terrain positions in non-infinite worlds. Return true if your generator has specific bounds, or false (the default) if the terrain extends indefinitely. The Unity Terrain Generator overrides this automatically using the TerrainData dimensions.

Chunk Layout

Each chunk is a flat array of 4096 voxels (16x16x16). Use these constants to calculate indices:

  • ONE_Y_ROW = 256 (16 x 16) - offset to move one row up in Y
  • ONE_Z_ROW = 16 - offset to move one row in Z
  • Index formula: index = y * ONE_Y_ROW + z * ONE_Z_ROW + x

These constants are available as protected fields in VoxelPlayTerrainGenerator.

Voxel Helpers

Use these methods on chunk.voxels[index] to set voxel data efficiently:

MethodUse for
SetFastOpaque(VoxelDefinition type)Solid terrain voxels (dirt, stone, sand). Fastest option - sets full opacity.
SetFastWater(VoxelDefinition type)Water and other fluid voxels.
Set(VoxelDefinition type)General-purpose setter. Slightly slower but handles any voxel type.
Set(VoxelDefinition type, Color32 tint)Same as above but also applies a color tint.

Example: Simple Flat Terrain Generator

using UnityEngine;
using VoxelPlay;

[CreateAssetMenu(menuName = "Voxel Play/Terrain Generators/Flat Terrain",
                 fileName = "FlatTerrainGenerator", order = 200)]
public class FlatTerrainGenerator : VoxelPlayTerrainGenerator {

    [Header("Flat terrain settings")]
    public int altitude = 40;
    public VoxelDefinition terrainVoxel;
    public Color32 voxelColor = new Color32(255, 255, 255, 255);

    public override bool PaintChunk(VoxelChunk chunk) {
        // Bottom Y position of this chunk in world space
        int chunkBottomY = FastMath.FloorToInt(
            chunk.position.y - VoxelPlayEnvironment.CHUNK_HALF_SIZE);

        // If the entire chunk is above our terrain, skip it
        if (chunkBottomY >= altitude) {
            return false;
        }

        // How many rows (in local Y) are solid inside this chunk
        int maxY = altitude - chunkBottomY;
        int lastVoxel = maxY * ONE_Y_ROW;
        if (lastVoxel > chunk.voxels.Length)
            lastVoxel = chunk.voxels.Length;

        // Fill solid voxels
        for (int k = 0; k < lastVoxel; k++) {
            chunk.voxels[k].Set(terrainVoxel, voxelColor);
        }

        // Mark surface chunks so VP computes lighting correctly
        int chunkTopY = (int)chunk.position.y
            + VoxelPlayEnvironment.CHUNK_HALF_SIZE;
        chunk.isAboveSurface = chunkTopY >= altitude;

        return true;
    }
}
Tip: Always set chunk.isAboveSurface = true for chunks at or above the terrain surface. Voxel Play uses this flag to optimize lighting calculations - without it, surface chunks may appear incorrectly lit.
Was this page helpful?