1
votes

I have lately been up to some terrain generation using the unity engine and C#, I was able to conquer most of the issues that I ran into but this one got me stuck without an efficient solution:

So before I explain my problem I'll explain what iv'e Achieved so far; By using perlin noise I have been able to generate a mesh and apply different heights to its vertices by combining multiple heightmaps. I'm also able to generate and destroy terrain chunks as well as manage the chunks' LOD based on the viewer's position, for optimization purposes. Next I attended to the biome area distribution system, for the area distribution I used a combination of 2 perlin noise functions representing temperture and moisture levels, with voronoi cell distribution, the world is divided into virtual squares by some modulo and another perlin noise function is used to create a function which returns a voronoi control point based on the virtual square it recieves as an input, so far just a voronoi control point distribution algorithm I had in mind, so far so good...

Next I wanted to make the terrain more interesting (It's designed for a survival game) so I started making my way towards having a unique noise function combination for each biome (Each voronoi cell) but this isn't going to be that easy... the seams between biomes will surely start to show and therefore a biome interpolation system is needed, at first i thought a simple weight function will do the work, meaning for each vertex calculate it's distance from adjacent biome cells and calculate the weight each cell has on that vertex: A value between 0 and 1, 0 = lowest effect(The biome point is really far away, too far to have any effect on the vertex); 1 = highest effect(The biome point is in the exact same position as the vertex, thus, it will have a maximum effect on the vertex's height and will be solely composited of the biome's unique noise function) and then calculate the sum of multiplying each biome's weight on the vertex by the biomes noise function at the vertex's position (Side note: The sum of all the biome points weights should always be 1).

Example of weight calculation for adjacent points:

An example of weight calculation for adjacent points

In the picture: The weight of the biome pointed by the blue arrow will be about 0.1f (Small value because it's far away from the sample point thus it will have minimal effect on the height of the sample point). The weight of the biome pointed by the green arrow will be about 0.45f. The weight of the biome pointed by the pink arrow will be about 0.45f (same as the green pointed biome because it's the same distance from the sample point). Orange point = the sample point which the weights will be calculated accurding to it.

There is a type of algorithm using noise called cellular noise, It's some kind of voronoi distribution with something similar to what I'm looking for but it doesn't take into account the effect of other adjacent biomes to the final value, meaning the furthur a point is away from any biome control point the lowest it's going to be but it doesn't blend different biomes which is a must have for me, I hope I explained well my issues with this algorithm. Anyway, here is an example.

An example of Cellular noise heightmap:

An example of Cellular noise heightmap

A small chunk of code to clerify the final vertex height:

float HeightAtPosition(float x, float z)
{
    //Returns an array containing information of all the adjacent biome 
    //control points(mainly their position and their biome type)
    BiomeControlPoint[] adjacentControlPoints = GetAdjacentBiomePoints(x, z);
    //Returns an array containing values between 0 and 1
    //Iputs: An array of all the adjacent biome points, a sample coordinate
    //Output: A float array containing the weight of each biome on the 
    //        coordinate(between 0 and 1)
    float[] weights = CalcWeights(adjacentControlPoints, new Vector2(x, z));
    float finalHeight = 0;
    for (int i = 0; i < adjacentControlPoints.Length; i++)
    {
        finalHeight += adjacentControlPoints[i] * weights[i];
    }
    return finalHeight;
}

This would have been a good solution if I could make these functions more performance efficient: GetAdjacentBiomePoints(x, z) and CalcWeights(adjacentControlPoints, new Vector2(x, z))

Another method I read about was bilinear interpolation but i didn't quite understand it's implementation so I'd be glad to learn from another source. this video discusses this method: https://www.youtube.com/watch?v=ujGW5y1x7JgCreating Minecraft in C++/ OpenGL - Part Four

I'd be glad to learn any new information and/or ideas from you :) Thank you in advance!

2
Is there a question in there, or are you just asking for opinions on something? - Ron Beyer
I would use the perlin noise function you already have to calculate biome properties, and then find the specific biome based on its properties. For example, you could use your noise function to calculate rainfall and temperature, and high rainfall/high temperature would be jungle, and low rainfall high temperature would be desert, etc. - Leo Bartkus
Leo Bartkus: I'm familiar with this method and I'm using this method combined with a voronoi diagram for the biome distribution. Ron Beyer: Yes... there is a question if anyone can think of a better way to interpolate between biomes noise functions rather than the way I specified, That way is alittle to performance costing in the future event of chunks generating fluently as the player moves around in the world, Maybe a bilinear interpulation but I'm not quite sure how to implement or rather how would it contribute for this cause at all so I'm hoping for some more experienced opinion. Thanks - Orr10c
Also I'm having some difficulties implementing the GetAdjacentBiomePoints() method, I want to only get the relevant points in an efficient way - Orr10c

2 Answers

0
votes

You could try a weighted voronoi diagram. My php implementation:https://tetramatrix.github.io/awvd/.

0
votes

You could go the other way:

  • generate temperature and moisture levels for each pixel
  • generate how much each biome matches those values
    • precalculate strength of each biome as a 2d lookup table?
  • take the X best matches
  • normalize the strengths (sum is 1)
  • your a unique noise function combination is weighted sum of the X best biomes

Personally I work with gridmaps and interpolate the biome's valus from a small map with large cells to a large map with small cells. Interpolation is much easier there:

Sorry that it is java.