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.

terrain with diagonal discontinuity

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];

Clearly not the most efficient way, but I need to get the basics working correctly before improving it.

  • \$\begingroup\$ This would likely be easier to troubleshoot if we could see the code that leads to this problem, allowing us to reproduce the issue ot test potential fixes. \$\endgroup\$
    – DMGregory
    Oct 26, 2020 at 1:18
  • \$\begingroup\$ Right, I added a link to the algorithm I'm using in the edit. \$\endgroup\$ Oct 26, 2020 at 1:26
  • \$\begingroup\$ Reviewing the code you've linked, it appears to be entirely methods for reading heights from the terrain. But your problem appears to be in writing appropriate heights to the terrain. The code you need to show us is the code you use to write those heights. \$\endgroup\$
    – DMGregory
    Oct 26, 2020 at 2:07
  • \$\begingroup\$ Thanks again, I have added the code I am using. Perhaps the problem lies with how I am flattening the data. If I use the overload in that repo that explicitly uses a float[ , ] array it works. But when I flatten it, I get that diagonal issue. \$\endgroup\$ Oct 26, 2020 at 11:30
  • \$\begingroup\$ Do you render 2 triangles for the whole patch ? I suggest you take the (monochrome) texture and use it's values to offset from a (regular) mesh in the vertex stage. Then such patterns can't happen if they are not in the texture. \$\endgroup\$
    – user144188
    Oct 26, 2020 at 11:46

1 Answer 1


For future reference, the problem causing the diagonal discontinuity was due to the sampling in the filtering function happening on an array whose rows were 1 position too short. Simply making sure that the value of the "resolution" passed to the function had the correct value (+1) fixed the problem.


You must log in to answer this question.

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