With any biome blending, your general goal is to produce a set of {biome, weight}
pairs where all the weights sum to one. You can then compute any blended value as a weighted sum of the individual biome values.
Your parameter space is essentially a Voronoi diagram, and you want to compute a border blending within the diagram.
One solution which produces smooth results is as follows:
- Find the distance
D
to the closest Voronoi point.
- Consider all Voronoi points within the range of
4R + D
, where R
is your blending radius.
- This is the maximum distance of any point that can affect the weights in the step to follow.
- You can omit this at first, but as you add more biomes the runtime complexity of the following steps will be O(N²).
- Initialize each point's weight at 1. Iteratively refine their weights as follows:
- Loop over every distinct pair of points
(A, B)
from the above query step.
- Find the dividing line between them, and calculate a signed distance
F
of the input point I
from the dividing line, such that it's positive towards B
and negative towards A
. F = dot(I - (A.position + B.position) / 2, B.position - A.position) / length(B.position - A.position)
.
- Divide by
R
so the radius covers the range [-1, 1]
within the blending radius, clamp the result so it can't go past those values, rescale it to [0, 1]
, and run it through a fade curve. H = fade(min(max(F/R, -1), 1) * 0.5 + 0.5)
where fade(t) = t*t*(3-2*t)
.
- Multiply B's weight by
H
, and A's weight by 1-H
.
- Divide each weight by the total.
In essence, the algorithm is performing individual clamped-smoothstep blending between each pair of points, then multiplying all of a given point's weights together. Between points A and B in the following illustration, the input (evaluation) coordinate is on A's side, so A would get a weight closer to one and B would get a weight closer to zero, as decided by how far the input point is from the dividing line. If the dividing line were completely out of the blending range, then A's weight in this calculation would be one and B's would be zero.
Then, since you're sampling the biome space with noise, which tends to have different slopes in different value ranges, you will likely want a way to control the blending size depending on which biomes are meeting. To do this, you can assign each biome its own radius value. To put that value to use, you might choose to compute the average every time you consider a pair of biomes in the above steps. If you're feeling adventurous, you can also try to devise a formula to automatically assign each biome a radius based on its position in the blending space.
Explanation for the 4R + D
consideration range: For a point B to be close enough to A that it could have a nonzero weight when pitted against it, the dividing line has to be within the blending radius of the input coordinate. For this to be the case, B must be less than 2R + D
distance away. Specifically, that's +R
for how far away the dividing line needs to be, then another +R
to move B far enough to place it there. Finally, for a point C to be able to modify the weight of the furthest possible considered B, add another 2R
to get 4R + D
. Note that once a point receives a zero weight as part of any pair calculation, its final weight will necessarily be zero.