为何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; }
问题根源
顶点索引计算错误:通过递增
vi变量追踪顶点索引的方式存在逻辑漏洞,在高分辨率下会触发索引越界:- 当
Resolution=512时,顶点总数为513*513=263169,最大有效索引为263168 - 循环到后期时,
vi + Resolution + 2的计算结果会超过最大索引,读取数组外的无效内存,导致渲染异常 - 256x256时
vi + 256 + 2仍小于最大有效索引,因此不会暴露问题
- 当
UV逻辑错误:重复的
else if (height < 0.4)条件导致uv3永远无法被赋值。CellMap覆盖问题:
cellMap[x / 4, y / 4] = cell会让4x4个四边形覆盖同一个数组位置,最终仅保留每个cell的最后一个四边形数据。
修复方案
- 重构顶点索引计算:移除循环中维护的
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; } }
- 修复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);
- 可选优化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
相关产品推荐
相关产品推荐

