Series: Procedural Generation
- Terrain Generation part 1: Voronoi noise - September 30, 2023
- Terrain Generation part 2: layering noise functions - October 03, 2023
Terrain Generation part 3: random continents - October 04, 2023
- Terrain Generation part 4: deriving temperature - October 06, 2023
Terrain Generation part 3: random continents
Where the last blog post left off was the idea of layering noise functions to create more natural looking terrain. That terrain looked like islands scattered aimlessly. That’s not nearly so much land mass as is wanted.
Next step is to slightly adjust the ratio of each noise function until the voronoi tiles more clearly show when something should be land.
This code here doesn’t display all function’s source but it should get the idea across of how the noise is composed.
let (width, height) = (512, 512);
let rng = rand::rngs::StdRng::seed_from_u64(20);
let basic_multi = ScalePoint::new(Billow::<Perlin>::new(20).set_lacunarity(3.0)).set_scale(3.0);
let normalized_noise = crate::utility::NormalizeNoise2::new(width as usize, height as usize, basic_multi);
let voronoi = crate::voronoi::Voronoi::new(width as usize, height as usize).set_variance(10);
let region_map = voronoi.generate(rng);
let mut regions_in_group = Vec::new();
for t in 0..voronoi.tiles_x {
regions_in_group.push(t);
regions_in_group.push(t + (voronoi.tiles_y - 1) * voronoi.tiles_x);
}
let region_map = region_map.with_region_group(regions_in_group);
let blur_noise = crate::utility::BlurNoise2::new(7, region_map);
let blur_noise = ScaleBias::new(blur_noise).set_scale(-1.0).set_bias(1.5);
let blend = Blend::new(normalized_noise, blur_noise, Constant::new(0.5));
That’s too much land mass now. But with the above method I can more evenly create land mass by setting entire voronoi tiles to scale towards land.
I’d like to have continents and irregular land mass. The simplest way to immediately see some results is to shuffle the order of voronoi tiles and take the first N. I can exclude the top and bottom layers by not even including them in the results to take from.
pub fn random_coverage(voronoi: Voronoi, coverage: f64, mut rng: impl RngCore) -> RegionMap {
let mut remaining_tiles = Vec::new();
for y in 1..voronoi.tiles_y - 1 {
for x in 0..voronoi.tiles_x {
remaining_tiles.push([x, y]);
}
}
remaining_tiles.shuffle(&mut rng);
let min_coverage = (coverage.clamp(0.0, 1.0) * (voronoi.tiles_x * voronoi.tiles_y) as f64).floor() as usize;
voronoi.generate(rng)
.with_default_level(0.0)
.with_region_group(
remaining_tiles.iter()
.take(min_coverage)
.map(|x| { x[0] + x[1] * voronoi.tiles_x }).collect())
}
With sampled noise, that doesn’t look so bad. That land mass is an interesting shape.
One problem is that the map isn’t really a cylinder. Wrapping the map around produces an artifact like this. Note that problem down for a later blog post.
So far, every map has been 512 tiles by 512 tiles, and an image representation of the same size.
Scaling up to 1400 tiles by 700 tiles brings out some weirdness. It’s still essentially the same map but stretched oddly and I don’t like the look of it.
Accounting for width and height differences in noise functions will take care of that.
let seed: u32 = 20;
let (width, height) = (860 as u32, 640 as u32);
let rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
let basic_multi = ScalePoint::new(Billow::<Perlin>::new(seed).set_lacunarity(width as f64 / 180.0)).set_scale(width as f64 / 180.0);
let normalized_noise = crate::utility::NormalizeNoise2::new(width as usize, height as usize, basic_multi);
let voronoi = Voronoi::new(width as usize, height as usize)
.set_variance(10)
.set_tiles(width as usize / 64, height as usize / 64);
let region_map = random_coverage(voronoi, 0.35, rng);
let blur_noise = crate::utility::BlurNoise2::new(4, region_map);
let blend = Blend::new(normalized_noise, blur_noise, Constant::new(0.38));
Commentary:
- That edge of map bordering cylinder isn’t quite right.
- Adjusting noise functions to wraparound would be the fix.
- The noise patterns are clearly evident and get more clearly shown when zoomed in.
- This map is an alien planet so I don’t much mind. In fact, making interesting water features is a good thing.
On the whole, that’s a good enough height map in order to start looking at other factors of terrain generation next time.
A few other generated samples from different seeded RNG:
Series: Procedural Generation
- Terrain Generation part 1: Voronoi noise - September 30, 2023
- Terrain Generation part 2: layering noise functions - October 03, 2023
Terrain Generation part 3: random continents - October 04, 2023
- Terrain Generation part 4: deriving temperature - October 06, 2023