Is there something wrong with terrain colliders?

I recently wrote a script that generates a move radius out of a mesh surrounding my character and wanted to modify it to make the move radius climb up and down terrain given a certain angle threshold. After lots of painstaking effort I came up with something that MOSTLY works, but for whatever reason I still get little bits and pieces that clip through the terrain and I don’t know why.

This is what it looks like when the terrain is active:

And this is what it looks like when the terrain is turned off:

In case the problem is something super obvious this is the code I’m using to generate the move radius

public class UnitScript : MonoBehaviour
{
    const float RADIUS = 10;
    const int ANGLE = 361;
    const float THRESHOLD = 45;
    Mesh mesh;
    public GameObject dot; //Ignore this. This is just a debugging tool to find the points of one of the lines

    void OnMouseDown()
    {
        //Generate move radius
        if (Input.GetMouseButtonDown(0) && mesh == null)
        {
            //Re-enable move radius's Mesh Renderer
            transform.GetChild(0).GetComponent<MeshRenderer>().enabled = true;

            //Declare vertices and triangles for the mesh
            Vector3[][] lines = new Vector3[ANGLE][];
            List<Vector3> vertices = new List<Vector3>();
            List<int> triangles = new List<int>();

            //Origin
            vertices.Add(Vector3.zero);

            ///*
            //Populate line vertices
            for (int i = 0; i < lines.Length; i++)
            {
                Vector3 direction = new Vector3(Mathf.Sin(Mathf.Deg2Rad * i), 0, Mathf.Cos(Mathf.Deg2Rad * i));
                lines[i] = CreateVertexArray(transform.position + Vector3.down, direction);
            }

            //Add line vertices to the master list and plot the points
            for (int y = 0; y < ANGLE; y++)
            {
                for (int x = 0; x < lines[y].Length; x++)
                {
                    vertices.Add(lines[y][x] - (transform.position + (Vector3.down * transform.position.y)));
                }
            }

            //Create triangles
            int processedVertices = 0;

            for (int l = 0; l < ANGLE - 1; l++)
            {
                for (int v = 0; v < Mathf.Max(lines[l].Length, lines[l + 1].Length); v++)
                {
                    //First triangle
                    if (v == 0)
                    {
                        triangles.Add(0);
                        triangles.Add(processedVertices + 1);
                        triangles.Add(processedVertices + lines[l].Length + 1);
                    }

                    //If there is no imbalance yet or really ever
                    else if (v < lines[l].Length && v < lines[l + 1].Length)
                    {
                        triangles.Add(processedVertices + v);
                        triangles.Add(processedVertices + v + 1);
                        triangles.Add(processedVertices + lines[l].Length + v);

                        triangles.Add(processedVertices + lines[l].Length + v);
                        triangles.Add(processedVertices + v + 1);
                        triangles.Add(processedVertices + lines[l].Length + v + 1);
                    }

                    //If the imbalance favors the main line
                    else if (v < lines[l].Length)
                    {
                        triangles.Add(processedVertices + lines[l].Length + lines[l + 1].Length);
                        triangles.Add(processedVertices + v);
                        triangles.Add(processedVertices + v + 1);
                    }

                    //If the imbalance favors the next line
                    else if (v < lines[l + 1].Length)
                    {
                        triangles.Add(processedVertices + lines[l].Length);
                        triangles.Add(processedVertices + lines[l].Length + v + 1);
                        triangles.Add(processedVertices + lines[l].Length + v);
                    }
                }

                processedVertices += lines[l].Length;
            }
            //*/

            //Create Mesh
            CreateMesh(vertices.ToArray(), triangles.ToArray());
        }
    }
    //Functions
    Vector3[] CreateVertexArray(Vector3 origin, Vector3 direction)
    {
        List<Vector3> returnValue = new List<Vector3>();
        float distance = RADIUS;
        RaycastHit hit;
        bool endLine = false;

        for (float angle = -THRESHOLD; angle <= THRESHOLD; angle++)
        {
            //Add in the Y dimension
            Vector3 combinedVector = direction + new Vector3(0, Mathf.Sin(Mathf.Deg2Rad * angle), 0);

            //Raycast the combined vector
            if (Physics.Raycast(origin, combinedVector, out hit))
            {
                if (hit.distance < distance && hit.distance > 0.1f)
                {
                    returnValue.Add(hit.point);
                    origin = hit.point - (direction * Mathf.Abs(GameObject.Find("Level").transform.position.y));
                    angle = -46;
                    distance -= hit.distance;
                }
                else if (hit.distance >= distance)
                {
                    returnValue.Add(origin + (combinedVector * distance));
                    break;
                }
            }
            else
            {
                returnValue.Add(origin + (combinedVector * distance));
                break;
            }

            //Break if the distance is maxed out
            if (endLine == true)
            {
                break;
            }
        }

        return returnValue.ToArray();
    }
    void CreateMesh(Vector3[] vertices, int[] triangles)
    {
        mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
        transform.GetChild(0).GetComponent<MeshFilter>().mesh = mesh;
    }
}

Terrain is not always rendered with a constant number of polygons. It has dynamic level-of-detail adjustment based on distance from camera. This means that high frequency features get smoothed at higher farther distances.

To see this for yourself, make a terrain with very detailed up/down noise on it, then put a Unity plane “in” the terrain so some of it clips through. Obviously make the plane different enough in color to see, then pull the camera back.

Ok, then how do I fix that?

You may be able to use multiple cameras and layers so that you know the blue circle is always drawn after the terrain.

Main camera sees terrain, second camera (clear flags to “clear depth only”) sees your circle, and if anyone else goes on top, have another camera for them.

Or you can probably do it with a custom shader that ignores Z depth, depending on how you’re doing stuff.

Ok, I have a second camera that moves along with the first and it has its clear flags set to depth only and nothing has changed. Now what?

Likewise I’m not sure if this is going to work anyway since I need the mesh collider to be clickable and I’m pretty sure that’s not going to happen if there’s still terrain in the way.