2d – Generating consistent terrain chunks with unpredictable per-chunk seeds

For this answer, I’m going to assume that the inputs to your biome-selection process are just the elevations (or other measures like temperature/moisture) output from your Perlin noise function. If you’re generating your biomes in a different way, be sure to edit your question to walk us through your current generation rules.

Perlin noise and similar functions are what’s called gradient noise: they pseudo-randomly select a gradient at each lattice point, using the point’s location and some seed information, and then interpolate a value between those gradients to fill the lattice cell between those corners.

So, as long as two chunks can agree on the gradients at those lattice points, then the rest will blend seamlessly. I show this in a couple of previous answers, for making a wrapping world and hexagonal tiles.

One way we can do this is to establish which chunk “owns” each lattice point. Say, a chunk owns every lattice point in its interior, and those lying exactly on its south and west edges. It north and east edges are owned by the neighbouring chunk though – they’re their south and west edges, respectively.

When we want to generate a pseudorandom gradient for a lattice point, we first check which chunk it’s in, and use that chunk’s seed in the pseudorandom selection. If we’ve never generated a seed for that chunk before, then we generate it now, on the fly, and save it. (That means you may have seeds stored for some chunks you’ve never visited, if they’re immediately adjacent to one that you have)

And that’s all – as long as the chunk you’re in has access to the seeds of the neighbouring chunks, then you can generate it in a way that will be seamlessly consistent with them, even if you’ve never generated their interiors yet.

You may also be interested in Deterministic Procedural Wave Function Collapse for another way of approaching this chunk consistency problem, through different procedural tools, but with the same core approach: make sure they agree about the inputs to the generation function at the edges where they meet, and the output will match up seamlessly.