You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何256x256以上的程序化网格模型三角化失效?

高分辨率地形三角化失效问题排查与修复

我用钻石方块算法制作程序化地形高度图,以下是生成模型的C#代码。该代码在16x16、32x32直到256x256的网格尺寸下运行正常,但512x512及以上尺寸时出现异常:

  • 256x256:模型完全正常
  • 512x512:从y=128行开始三角化失效,地形底面出现异常条状结构

已确认高分辨率下生成的顶点数据无问题,可确定是三角化环节出错。

public Map GenerateMap()
{
    Mesh mapMesh = new();

    vertices = new Vector3[(Resolution + 1) * (Resolution + 1)];
    Vector2[] uv1 = new Vector2[vertices.Length];
    Vector2[] uv2 = new Vector2[vertices.Length];
    Vector2[] uv3 = new Vector2[vertices.Length];
    DiamondSquare diamondSquare = new(Resolution, Roughness, Seed, HeightLevels);
    float[,] heightFloatMap = diamondSquare.DoDiamondSquare();
    tex = new Texture2D(Resolution, Resolution);

    for (int y = 0, i = 0; y <= Resolution; y++)
    {
        for (int x = 0; x <= Resolution; x++, i++)
        {
            float height = heightFloatMap[x, y];
            vertices[i] = new Vector3(x * CellSize.x, height * CellSize.y, y * CellSize.z);
            tex.SetPixel(x, y, new Color(height, height, height, 1));
            if (height == 0)
                uv1[i] = new Vector2(vertices[i].x, vertices[i].z);
            else if (height < 0.4)
                uv2[i] = new Vector2(vertices[i].x, vertices[i].z);
            else if (height < 0.4) // 条件重复,uv3永远不会被赋值
                uv3[i] = new Vector2(vertices[i].x, vertices[i].z);
        }
    }
    mapMesh.vertices = vertices;
    mapMesh.uv = uv1;
    mapMesh.uv2 = uv2;

    int[] triangles = new int[Resolution * Resolution * 6];
    Cell[,] cellMap = new Cell[Resolution / 4, Resolution / 4];
    for (int ti = 0, vi = 0, y = 0; y < Resolution; y++, vi++)
    {
        for (int x = 0; x < Resolution; x++, ti += 6, vi++)
        {
            triangles[ti] = vi;
            triangles[ti + 3] = triangles[ti + 2] = vi + 1;
            triangles[ti + 4] = triangles[ti + 1] = vi + Resolution + 1;
            triangles[ti + 5] = vi + Resolution + 2;

            Vector3[] cellVerts = new Vector3[]
                {
                    vertices[vi], vertices[vi + 1], vertices[vi + Resolution + 1], vertices[vi + Resolution + 2]
                };
            Cell cell = new(new Vector2Int(x, y), cellVerts, CalculateCellGeometry(cellVerts));
            cellMap[x / 4, y / 4] = cell;
        }
    }
    mapMesh.triangles = triangles;

    mapMesh.RecalculateNormals();
    mapMesh.RecalculateTangents();
    mapMesh.RecalculateBounds();

    Map map = new(mapMesh, cellMap, heightFloatMap, vertices);
    return map;
}

问题根源

  1. 顶点索引计算错误:通过递增vi变量追踪顶点索引的方式存在逻辑漏洞,在高分辨率下会触发索引越界:

    • 当Resolution=512时,顶点总数为513*513=263169,最大有效索引为263168
    • 循环到后期时,vi + Resolution + 2的计算结果会超过最大索引,读取数组外的无效内存,导致渲染异常
    • 256x256时vi + 256 + 2仍小于最大有效索引,因此不会暴露问题
  2. UV逻辑错误:重复的else if (height < 0.4)条件导致uv3永远无法被赋值。

  3. CellMap覆盖问题:cellMap[x / 4, y / 4] = cell会让4x4个四边形覆盖同一个数组位置,最终仅保留每个cell的最后一个四边形数据。


修复方案

  1. 重构顶点索引计算:移除循环中维护的vi变量,直接通过y和x计算起始顶点索引,彻底避免递增错误:
// 重构三角化循环部分
int[] triangles = new int[Resolution * Resolution * 6];
Cell[,] cellMap = new Cell[Resolution / 4, Resolution / 4];
for (int ti = 0, y = 0; y < Resolution; y++)
{
    for (int x = 0; x < Resolution; x++, ti += 6)
    {
        // 直接计算当前四边形的起始顶点索引,逻辑清晰且无错误
        int vi = y * (Resolution + 1) + x;

        // 重新整理三角化索引,可读性更强
        triangles[ti] = vi;
        triangles[ti + 1] = vi + Resolution + 1;
        triangles[ti + 2] = vi + 1;

        triangles[ti + 3] = vi + 1;
        triangles[ti + 4] = vi + Resolution + 1;
        triangles[ti + 5] = vi + Resolution + 2;

        Vector3[] cellVerts = new Vector3[]
            {
                vertices[vi], vertices[vi + 1], vertices[vi + Resolution + 1], vertices[vi + Resolution + 2]
            };
        Cell cell = new(new Vector2Int(x, y), cellVerts, CalculateCellGeometry(cellVerts));
        cellMap[x / 4, y / 4] = cell;
    }
}
  1. 修复UV判断逻辑:修正重复条件,确保uv3能正确赋值:
if (height == 0)
    uv1[i] = new Vector2(vertices[i].x, vertices[i].z);
else if (height < 0.4)
    uv2[i] = new Vector2(vertices[i].x, vertices[i].z);
else
    uv3[i] = new Vector2(vertices[i].x, vertices[i].z);
  1. 可选优化CellMap:如果需要存储每个cell的完整数据,可调整为收集每个cell内的所有四边形,而非直接覆盖:
// 示例:将cellMap改为存储List<Cell>
List<Cell>[,] cellMap = new List<Cell>[Resolution / 4, Resolution / 4];
// 初始化每个位置的List
for (int cy = 0; cy < Resolution / 4; cy++)
{
    for (int cx = 0; cx < Resolution / 4; cx++)
    {
        cellMap[cx, cy] = new List<Cell>();
    }
}
// 填充时添加而非覆盖
cellMap[x / 4, y / 4].Add(cell);

内容的提问来源于stack exchange,提问作者Ryan Humphreys

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 02:41:26