I'm using Bilinear filtering to interpolate perlin-noise based height data. However, when I render the resulting plane mesh, there is this obvious diagonal discontinuity in the generated terrain.
See Edit below. I'm guessing it could either be an error in how I'm interpolating the data, or in how I am assigning the heights back to the plane vertices. But I haven't been able to fix it. Anybody has seen this type of error? What could it be?
I'm using the bilinear algorithm technique found here. My vertices step laid bottom up, left to right (I.e. vertex 0 is at the bottom left).
Edit: I have tried using a heightmap texture instead of noise generated data and I think the problem lies in how I am flattening the 2D texture data into a 1D array. If I use the overload in those methods that explicitly uses a float[,] array it works with a real texture. If I convert the same texture to a 1D array I see the diagonal discontinuity.
This is the code I'm using to flatten the data
public static float[] ConvertToXYflat(Color[] data, int res)
{
float[] converted = new float[data.Length];
int idx = 0;
for (int i = 0; i < res; i++)
{
for (int j = 0; j < res; j++)
{
converted[idx++] = data[(i * res) + j].r;
}
}
return converted;
}
The unity docs say that the texture pixels are laid row by row, left to right, bottom to top. That is also the pattern that my vertices have.
This is how the array of heights is constructed.
// world size dimensions of the plane mesh
Vector2 dimensions = new Vector2(terrainMesh.Width, terrainMesh.Height);
int resolution = 1023;
for (int i = 0; i < positions.Length; i++)
{
newHeights[i] = TerrainFilters.TerrainBilinear(nativeHM, positions[i].x, positions[i].z, MaxHeight, -dimensions/2, dimensions, resolution-1);
}
Filtering method:
public static float TerrainBilinear(NativeArray<float> heightMap, float worldX, float worldZ, float height, Vector2 worldSize, Vector2 dimensions, int resolution)
{
float x = ((worldZ - worldSize.y) * resolution) / dimensions.x;
float z = ((worldX - worldSize.x) * resolution) / dimensions.y;
int fx = (int)math.floor(x);
int cx = fx + 1;
int fz = (int)math.floor(z);
int cz = fz + 1;
int res = resolution + 1;
float hx0z0 = heightMap[fx * res + fz] * height;
float hx1z0 = heightMap[cx * res + fz] * height;
float hx0z1 = heightMap[fx * res + cz] * height;
float hx1z1 = heightMap[cx * res + cz] * height;
return hx0z0 + (hx1z0 - hx0z0) * (x - (int)x) + (hx0z1 - hx0z0) * (z - (int)z) + (hx0z0 - hx1z0 - hx0z1 + hx1z1) * (x - (int)x) * (z - (int)z);
}
Then I iterate a second time and add those heights to the original plane vertices like so:
public void UpdateHeights(float[] heights)
{
Vector3[] newVertices = new Vector3[vertices.Length];
Array.Copy(vertices, newVertices, vertices.Length);
for (int i = 0; i < heights.Length; i++)
{
newVertices[i].y += heights[i];
}
mesh.SetVertices(newVertices);
mesh.RecalculateNormals();
}
Clearly not the most efficient way, but I need to get the basics working correctly before improving it.