I'm developping a procedural terrain generator in Unity 2020.3, using Sebastian Lague's tutorials and knowledge i've gathered through college projects. The heightmap looks just fine, and i had no issues in 2D, but upon generating the Meshes, i'm getting these weird stairs-like shapes: https://i.stack.imgur.com/xGOdf.png
Debugging that kind of issue has proven quite hard and i spent a few hours not understanding what's wrong already. Displaying the values of my heightmap does not show any issue, values are what i would expect from perlin noise.
I need help understanding what's wrong.
For reference, the noise generation function:
public static float[,] GenerateNoise(int width, int height, int octaves, float persistance, float lacunarity, float scale, Vector2 offset, float redistribution, int seed,
bool islandMode, float waterCoefficient, float warping1, float warping2
) {
float[,] results = new float[width, height];
float begin = Time.realtimeSinceStartup;
//seed to have the possibility to recreate a noisemap
System.Random randomGenerator = new System.Random(seed);
//random offset
Vector2[] octaveOffsets = new Vector2[octaves];
for(int i = 0; i < octaves; i++)
{
float offsetX = randomGenerator.Next(-100000, 100000) + offset.x;
float offsetY = randomGenerator.Next(-100000, 100000) + offset.y;
octaveOffsets[i] = new Vector2(offsetX, offsetY);
}
for (float y = 0; y < width; y++) {
for (float x = 0; x < height; x++) {
// Initial values
float amplitude = 1;
float frequency = 1;
float noiseValue = 0;
// the FBM algorithm to generate layers
for (int i = 0; i < octaves; i++) {
float xCoord = x / width * scale * frequency + octaveOffsets[i].x;
float yCoord = y / height * scale * frequency + octaveOffsets[i].y;
//warping reusing code from https://www.iquilezles.org/www/articles/warp/warp.htm
//add 1st level of warping
Vector2 q = new Vector2(Mathf.PerlinNoise(xCoord, yCoord),
Mathf.PerlinNoise(xCoord + 5.2f, yCoord + 1.3f));
//second level of warping
Vector2 r = new Vector2(Mathf.PerlinNoise(xCoord + warping1 * q.x + 1.7f, yCoord + warping1 * q.y + 9.2f),
Mathf.PerlinNoise(xCoord + warping1 * q.x + 5.2f, yCoord + warping1 * q.y + 1.3f));
noiseValue += Mathf.PerlinNoise(xCoord + warping2 * r.x, yCoord + warping2 * r.y) * amplitude;
frequency *= lacunarity;
amplitude *= persistance;
}
//redistribution to allow creation of flat areas if need be
float finalValue = Mathf.Pow(noiseValue, redistribution);
//create an island shape by lowering noise value the further you are from the middle of the map
if (islandMode)
finalValue = finalValue - Vector2.Distance(new Vector2(x, y), new Vector2(width / 2, height / 2)) / (width * waterCoefficient);
results[(int) x, (int) y] = finalValue;
}
}
Debug.Log("time to generate heightmap: " + (Time.realtimeSinceStartup - begin));
return results;
}
And the Mesh generation function:
public static Mesh GenerateMesh(float[,] noisemap, float minHeight, float maxHeight, int width, int height, float pointsPerUnit) {
float begin = Time.realtimeSinceStartup;
Mesh mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
int nx = noisemap.GetLength(1);
int ny = noisemap.GetLength(0);
Vector3[] vertices = new Vector3[nx * ny];
//calculate vertices
for (int y = 0; y < ny; y++)
{
//only write the point if he is not at the end of a line (as it would not have a point to its right)
for (int x = 0; x < nx; x++)
{
vertices[y * nx + x] = new Vector3((float) x / pointsPerUnit, noisemap[x, y] * maxHeight, (float) y / pointsPerUnit);
}
}
Vector2[] uvs = new Vector2[vertices.Length];
for (int i = 0; i < uvs.Length; i++)
{
uvs[i] = new Vector2(vertices[i].x / width, vertices[i].z / height);
}
List<int> triangles = new List<int>();
//skips the first line as it has to points above
for (int y = 1; y < ny; y++)
{
//only write the point if he is not at the end of a line (as it would not have a point to its right)
for (int x = 0; x < nx - 1; x++)
{
int current = y * nx + x; // the "number" of the current point in a "1D" fashion
//write current, diagonal, above and current, right, diagonal
int above = current - ny; //the point above the current
int rDiagonal = above + 1; //the point in the right diagonal of the current
int right = current + 1;//the point to the right of the current
triangles.Add(current);
triangles.Add(rDiagonal);
triangles.Add(above);
triangles.Add(current);
triangles.Add(right);
triangles.Add(rDiagonal);
}
}
mesh.SetVertices(vertices);
mesh.triangles = triangles.ToArray();
mesh.SetUVs(0, uvs);
mesh.RecalculateNormals();
Debug.Log("time to generate mesh: " + (Time.realtimeSinceStartup - begin));
return mesh;
}
```