编辑高度图后局部重计算法线的问题及解决(C#/MonoGame)
地形编辑器法线局部更新问题与解决
我使用C#和MonoGame开发了一款地形编辑器。编辑地形后,希望仅重新计算与位置发生变化的三角形相关联的顶点的法线,但遇到了问题:编辑区域周围总会出现一圈法线不匹配的“环”。而重新计算网格的所有法线时,显示就完全正常。
效果对比
仅计算受影响三角形法线的效果:

正确效果(通过计算所有顶点法线实现,计算逻辑完全一致):

初始问题代码
最初用于仅重新计算受影响顶点法线的代码如下:
private void RecalculateNormals(Terrain terrain, List<int> uniqueVertexIndex) { List<int[]> totalTriangles = new List<int[]>(); List<int> verticesToNormalize = new List<int>(); foreach (int vertexIndex in uniqueVertexIndex) { Console.WriteLine("Vertex Position Changed: " + vertexIndex); foreach (int[] triangle in terrain.Collider.lookupTrianglesAttachedToVertex(vertexIndex)) { bool skip = false; foreach (int[] testTriangle in totalTriangles) if (triangle[0] == testTriangle[0]) { skip = true; } if (!skip) { totalTriangles.Add(triangle); foreach (int index in triangle) { if (!verticesToNormalize.Contains(terrain.Mesh.Indices[index])) { verticesToNormalize.Add(terrain.Mesh.Indices[index]); } } } } } foreach (int vertex in verticesToNormalize) { terrain.Mesh.Vertices[vertex].Normal = new Vector3(0, 0, 0); Console.WriteLine("Vertex Updating: " + vertex); } foreach (int[] triangle in totalTriangles) { int index1 = terrain.Mesh.Indices[triangle[0]]; int index2 = terrain.Mesh.Indices[triangle[1]]; int index3 = terrain.Mesh.Indices[triangle[2]]; Vector3 side1 = terrain.Mesh.Vertices[index3].Position - terrain.Mesh.Vertices[index2].Position; Vector3 side2 = terrain.Mesh.Vertices[index2].Position - terrain.Mesh.Vertices[index1].Position; Vector3 normal = Vector3.Cross(side1, side2); terrain.Mesh.Vertices[index1].Normal += normal; terrain.Mesh.Vertices[index2].Normal += normal; terrain.Mesh.Vertices[index3].Normal += normal; Console.WriteLine("Recalculated Normals on Triangle: " + triangle[0] + " " + index1 + " | " + triangle[1] + " " + index2 + " | " + triangle[2] + " " + index3); } foreach (int vertex in verticesToNormalize) terrain.Mesh.Vertices[vertex].Normal.Normalize(); }
问题解决与修正代码
经过进一步调试,发现问题核心是未考虑位置未变化但关联三角形受影响的顶点,同时移除对未变化顶点的法线累加操作后,问题已完美解决!修正后的代码如下:
private void RecalculateNormals(Terrain terrain, List<int> uniqueVertexIndex) { List<int[]> totalTriangles = new List<int[]>(); List<int> verticesToNormalize = new List<int>(); foreach (int vertexIndex in uniqueVertexIndex) { //Console.WriteLine("Selected Vertex: {0}", vertexIndex); foreach (int[] triangle in terrain.Collider.lookupTrianglesAttachedToVertex(vertexIndex)) { bool skip = false; foreach (int[] testTriangle in totalTriangles) { if (testTriangle[0] == triangle[0]) { skip = true; } } if (!skip) { totalTriangles.Add(triangle); } foreach (int index in triangle) { if (!verticesToNormalize.Contains(terrain.Mesh.Indices[index])) { //Console.WriteLine("Adding Vertex: " + terrain.Mesh.Indices[index]); verticesToNormalize.Add(terrain.Mesh.Indices[index]); } } } } foreach (int vertex in verticesToNormalize) { //Console.WriteLine("Vertex To Update: {0}", vertex); foreach (int[] triangle in terrain.Collider.lookupTrianglesAttachedToVertex(vertex)) { bool skip = false; foreach (int[] testTriangle in totalTriangles) { if (testTriangle[0] == triangle[0]) skip = true; //Console.WriteLine("Skipping Triangle: {0} | Compared to Existing Triangle: {1}", triangle[0], testTriangle[0]); } if (!skip) { //Console.WriteLine("Not Skipping Triangle {0}", triangle[0]); totalTriangles.Add(triangle); } } } Dictionary<int,int> NormalUpdateCount = new Dictionary<int, int>(); foreach (int vertex in verticesToNormalize) { terrain.Mesh.Vertices[vertex].Normal = new Vector3(0, 0, 0); //Console.WriteLine("Vertex Updating: " + vertex); NormalUpdateCount.Add(vertex, 0); } foreach (int[] triangle in totalTriangles) { int index1 = terrain.Mesh.Indices[triangle[0]]; int index2 = terrain.Mesh.Indices[triangle[1]]; int index3 = terrain.Mesh.Indices[triangle[2]]; Vector3 side1 = terrain.Mesh.Vertices[index3].Position - terrain.Mesh.Vertices[index2].Position; Vector3 side2 = terrain.Mesh.Vertices[index2].Position - terrain.Mesh.Vertices[index1].Position; Vector3 normal = Vector3.Cross(side1, side2); // terrain.Mesh.Vertices[index1].Normal += normal; // terrain.Mesh.Vertices[index2].Normal += normal; // terrain.Mesh.Vertices[index3].Normal += normal; if (verticesToNormalize.Contains(index1)) { NormalUpdateCount[index1] += 1; terrain.Mesh.Vertices[index1].Normal += normal; } if (verticesToNormalize.Contains(index2)) { NormalUpdateCount[index2] += 1; terrain.Mesh.Vertices[index2].Normal += normal; } if (verticesToNormalize.Contains(index3)) { NormalUpdateCount[index3] += 1; terrain.Mesh.Vertices[index3].Normal += normal; } } // foreach (KeyValuePair<int, int> kvp in NormalUpdateCount) // { // Console.WriteLine("Vertex {0} updated with {1} normal additions", kvp.Key, kvp.Value); // } foreach (int vertex in verticesToNormalize) { terrain.Mesh.Vertices[vertex].Normal.Normalize(); } }
内容的提问来源于stack exchange,提问作者servvs
相关产品推荐
相关产品推荐

