Mastodon Politics, Power, and Science: Fractal World Map Generation Implementation Guide

Thursday, December 4, 2025

Fractal World Map Generation Implementation Guide

 J. Rogers, SE Ohio

This actually works, I implemented it inside a campaign manger, and it works great.  Still needs a little work on the erosian going over the edge of the map and wraapping around, it seems to want to make hard edges against the edge of the map. 

Overview

This guide demonstrates how to create realistic world maps using fractal terrain generation, erosion simulation, and adjustable water levels.

Core Concepts

1. Fractal Terrain Generation

Use Diamond-Square Algorithm or Perlin/Simplex Noise to create the base heightmap:

Diamond-Square Algorithm:

  • Start with a grid (power of 2 + 1, e.g., 513x513)
  • Set corner values randomly
  • Recursively subdivide:
    • Diamond step: Calculate center of squares from corners
    • Square step: Calculate edge midpoints from adjacent points
    • Add random displacement that decreases with each iteration
    • Displacement = initial_range × (roughness ^ iteration)

Multi-Octave Noise (Recommended):

height = 0
amplitude = 1
frequency = 1

for each octave:
    height += noise(x * frequency, y * frequency) * amplitude
    amplitude *= persistence (0.5 typical)
    frequency *= lacunarity (2.0 typical)

2. Terrain Features Through Weathering

Hydraulic Erosion

Simulates water flow carving valleys and depositing sediment:

For each iteration:
    1. Drop water particle at random position
    2. Trace downhill path:
       - Calculate gradient (steepest descent)
       - Move particle in that direction
       - Erode terrain: height -= erosion_rate × slope × sediment_capacity
       - Carry sediment with particle
       - Deposit when slope decreases or capacity exceeded
    3. Evaporate (deposit remaining sediment)

Key Parameters:

  • Erosion rate: 0.3 - 0.5
  • Deposition rate: 0.1 - 0.3
  • Sediment capacity: 4.0 - 8.0
  • Iterations: 100,000 - 500,000

Thermal Erosion

Simulates gravity-driven landslides:

For each cell:
    max_height_diff = 0
    target_neighbor = none
    
    for each neighbor:
        diff = current_height - neighbor_height
        if diff > threshold AND diff > max_height_diff:
            max_height_diff = diff
            target_neighbor = neighbor
    
    if target_neighbor exists:
        amount = talus_coefficient × (max_height_diff - threshold)
        transfer sediment from current to target

Key Parameters:

  • Talus angle threshold: 0.5 - 1.0
  • Transfer coefficient: 0.5

3. Adding Natural Variation

Multi-Scale Noise Layering

final_height = base_fractal_height
    + large_scale_noise × 0.3      // Continental features
    + medium_scale_noise × 0.15    // Mountain ranges
    + small_scale_noise × 0.05     // Local variation

Noise Types to Combine:

  • Perlin Noise: Smooth, natural-looking
  • Simplex Noise: Faster, less directional artifacts
  • Worley Noise: Cell-like patterns (good for craters)
  • Turbulence: Distorted noise for chaos

4. Water Level System

Height Normalization

1. Find min and max heights in terrain
2. Normalize all heights to 0-1 range:
   normalized_height = (height - min) / (max - min)

Water Level Mapping

water_percentage (0-100) → water_level (0-1)

For each pixel:
    if normalized_height < water_level:
        render as water (ocean/sea)
        depth = water_level - normalized_height
        color based on depth (darker = deeper)
    else:
        render as land
        elevation = normalized_height - water_level
        color based on elevation (green→brown→white)

Realistic Water Levels:

  • 0-20%: Dry world, scattered lakes
  • 30-40%: Mars-like
  • 50-70%: Earth-like (70% is Earth's coverage)
  • 80-90%: Archipelago world
  • 95-100%: Water world

Implementation Steps

Step 1: Generate Base Terrain

grid = create 2D array (1024x1024 or higher)
apply diamond-square or multi-octave noise
result: rough heightmap with fractal properties

Step 2: Apply Erosion (Multiple Passes)

// Hydraulic erosion for rivers and valleys
for 200,000 iterations:
    simulate water droplet erosion

// Thermal erosion for realistic slopes
for 50 iterations:
    apply thermal weathering to entire map

Step 3: Add Detail Noise

detail = perlin_noise(x × 8, y × 8) × 0.03
turbulence = abs(perlin_noise(x × 16, y × 16)) × 0.02
heightmap += detail + turbulence

Step 4: Normalize Heights

min_height = find_minimum(heightmap)
max_height = find_maximum(heightmap)

for each point:
    heightmap[x][y] = (heightmap[x][y] - min_height) / (max_height - min_height)

Step 5: Apply Water Level

water_threshold = water_percentage / 100.0

for each point:
    if heightmap[x][y] < water_threshold:
        type = WATER
        depth = water_threshold - heightmap[x][y]
    else:
        type = LAND
        elevation = heightmap[x][y] - water_threshold

Rendering Tips

Color Gradients

Water (by depth):

  • Shallow (0.0-0.1): Light cyan #7DD3C0
  • Medium (0.1-0.3): Ocean blue #2A52BE
  • Deep (0.3+): Dark blue #001440

Land (by elevation):

  • Low coastal: Green #3A7D44
  • Mid elevation: Brown #8B7355
  • High elevation: Gray #A9A9A9
  • Snow peaks: White #F0F8FF

Enhancing Realism

  1. Shadows: Calculate lighting based on normal vectors
  2. Contour lines: Draw elevation lines every 0.1 units
  3. Biomes: Use temperature + moisture maps for vegetation
  4. Atmospheric perspective: Fade distant terrain

Performance Optimization

  • Use GPU shaders for real-time rendering
  • Generate terrain in chunks for large worlds
  • Cache erosion results (computationally expensive)
  • Use quadtree LOD for display
  • Multithreading for erosion simulation

Example Pseudocode

function generateWorld(size, water_percentage):
    // 1. Base terrain
    heightmap = diamondSquare(size, roughness=0.7)
    
    // 2. Add octaves
    for octave in [1, 2, 4, 8]:
        heightmap += perlinNoise(size, frequency=octave) * (0.5 ^ octave)
    
    // 3. Erode
    heightmap = hydraulicErosion(heightmap, iterations=200000)
    heightmap = thermalErosion(heightmap, iterations=50)
    
    // 4. Add detail
    heightmap += perlinNoise(size, frequency=16) * 0.02
    
    // 5. Normalize
    heightmap = normalize(heightmap, 0, 1)
    
    // 6. Apply water
    water_level = water_percentage / 100.0
    world_map = applyWater(heightmap, water_level)
    
    return world_map

Recommended Libraries

JavaScript:

  • Three.js (3D visualization)
  • Canvas API (2D rendering)
  • simplex-noise.js

Python:

  • NumPy (array operations)
  • Matplotlib (visualization)
  • noise library (Perlin/Simplex)
  • Pillow (image output)

C++:

  • libnoise
  • OpenGL/Vulkan (rendering)
  • FastNoise2

Further Enhancements

  • Plate tectonics: Simulate continental drift
  • Climate simulation: Temperature and wind patterns
  • Vegetation: Distribute based on climate
  • Cities: Place settlements near water/resources
  • Orbital parameters: Affect climate zones

No comments:

Post a Comment

Progress on the campaign manager

You can see that you can build tactical maps automatically from the world map data.  You can place roads, streams, buildings. The framework ...