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
- Shadows: Calculate lighting based on normal vectors
- Contour lines: Draw elevation lines every 0.1 units
- Biomes: Use temperature + moisture maps for vegetation
- 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