I have a noise function for altitude: float getAltitude(x, z) and noise function for humidity: float getHumidity(x, z) and at each cell of my terrain I calculate a (altitude, humidity) point, and then find which on of my predefined biome (altitude, humidity) points it's closest to - that gives me the biome the point is in. So then I use my particular biome's elevation noise function to set the elevation of the terrain at that point. That works great. I am having problems blending biomes. I tried setting up a BIOME_BLEND_RADIUS value and if the difference in distances to 2 (or more) biome points is within that BIOME_BLEND_RADIUS I interpolate them. My issue is that interpolation - I can't get it to work. I would appreciate some help with this, thanks.

edit: I tried interpolating two biomes at a time. I found distance1 = distance from input point to closest biome point in (altitude, humidity) space and distance2 which is the equivalent fro second closest biome. Then if (distance2 - distance1 < BLEND_RADIUS) I did influence1 = (distance2 - distance1) / BLEND_RADIUS / 2 + 0.5f; and influence2 = 1.0f - influence1; and otherwise influence1 = 1.0f and influecne2 = 0.0f; This approach worked for blending two biomes but I didn't know how to expand it to more than that. Then I switched over to using my BLEND_RADIUS to just finding n closest biome points and then finding distances to each, normalizing them and using those normalized distances as influences of each biome. That approach blends nicely but causes bleed-over.


1 Answer 1


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.

Two points A and B with a dividing line between. An input point lies witin the blending range of the dividing line, closer to A than to B.

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.

The blending result. Multiple differently-colored cells fade smoothly into their neighbors across fixed-width borders.

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.

  • \$\begingroup\$ I have a few questions. First: I find closest point and the distance to it is D, then I find all points within 4R + D from input point. I can't understand the rest of what you said in that sub-bullet point. Second, after finding all the points within 4R + D of input point I am supposed to iterate through every possible pair of those points? If I have {A, B, C} should I do both (A, B) and (B, A). I guess I don't really understand what this algorithm is trying to do "semantically", could you elaborate on that (or point me to some resource you find useful on this)? Thank you. \$\endgroup\$
    – BogdanB
    Commented Jan 7 at 23:19
  • \$\begingroup\$ It's just (A,B) not also (B,A). I updated the answer to be more clear on this and the consideration range, and added a summary+graphic. \$\endgroup\$
    – KdotJPG
    Commented Jan 8 at 3:35
  • \$\begingroup\$ It may be useful to consider the Delaunay triangulation that's the dual of the Voronoi diagram. Then you can find which triangle your sample point falls within, and blend just three inputs, rather than iterating over all points in a radius. \$\endgroup\$
    – DMGregory
    Commented Jan 8 at 3:43
  • \$\begingroup\$ Thank you for the great response. I understand what the algorithm does now, I will try to implement it. \$\endgroup\$
    – BogdanB
    Commented Jan 8 at 7:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .