I've started work on generating terrain. I've settled on world sizes of 64,000x64,000, 48,000x48,000, and 32,000x32,000 tiles. For speed and memory efficiency I'm going to generate terrain data at 1/4 that detail and then scale it up. That means I'll be generating between 8,000^2 and 16,000^2 terrain data points.

My plan is to generate some nice looking noise, and use that as a height map...pretty standard for game design. I've tested out a number of algorithms: Different flavors of Perlin, Simplex, OpenSimplex, and fractional Brownian motion.

Regarding libraries, at first I did a lot of experimentation with noise-rs. It's really nice and quite flexible. When I wanted to scale up the size of the noise maps produced it got quite slow, however. So I transitioned to simdnoise. Noise-rs has a really useful utility for rendering noise maps here, so I refactored it to work with bytes instead of floating points, which works better for my current workflow. Any color renders below were produced using a customized version of color_gradient.rs from noise-rs.

I settled on fractional Brownian motion(fBm) because I like how it looks and how flexible it is. Here's a scaled down version of 8,000^2 fBm rendered in greyscale:

fBm

Just looks like a cloud, right? Well, if we imagine that black is the lowest elevation, white is the highest elevation, and render the varying "heights" of each pixel according to a handful of colors to represent water, land, grass, mountains, snow, etc. (10 base colors + blending)...we get a result like this:

fbm_terrain88

That's with water level set at 88 Z levels out of 256. It's kinda cool. Sort of get a shape of the continent with bodies of water. The mountains kinda suck though. They're just clouds pasted on top of the land.

Various noise functions can be tweaked to produce ridge patterns. For instance here's one that I scaled down from 8,000^2, rendered in greyscale:

ridge

Here's what it looks like if it's rendered in color as before, with water level at 88:

ridge_terrain88

Yikes. It's pretty ugly, but the mountain ranges are more realistic with ridges that look decent. What if I blended the two noise maps together, and then rendered the result? Simdnoise returns a Vec<f32> for generated noise maps.

Here's my code for blending two maps (dest is one of the noise maps being blended, it will be overwritten):

fn blend(dest: &mut Vec<f32>, other: &Vec<f32>, amount: f32) {
    for x in 0..dest.len() {
        if other[x] > dest[x] {
            dest[x] = (other[x]-dest[x]).mul_add(amount, dest[x]);
        }
        else {
            dest[x] = (dest[x]-other[x]).mul_add(amount, other[x]);
        }
    }
}

Pretty basic, but here's the results for 0.5 blend from 48 water level to 128 water level in 20 Z level increments:

blended_terrain48

blended_terrain68

blended_terrain88

blended_terrain108

blended_terrain128

I'm pretty happy with the shape of the worlds I'm generating now. But the mountains still need a little love, and I have to figure out where to place rivers. So next post I'll be playing around with simulating hydraulic erosion.