计算在接缝处结束的2个网格之间的法线

我的任务

我目前正在为Unity3D创建一个地形,专门用于运行应用程序的内存不足的移动设备。 允许大小为15.000 x 15.000千米,高度为-1.000米到10.000米的地形,它的唯一限制是硬盘上的空间。

情况

现在一切都运行正常,除了不正确地计算不同网格之间的法线(每个网格具有细分级别)。 这是两张可视化问题的图片:

带有显示三角形的网格仅与法线网格

问题仅发生在从一个细分级别到另一个细分级别的转换中。 如果两个网格具有相同的级别,则效果很好。 我首先想到在计算法线时我会错过一些面孔,但似乎它们都包含在计算中。

一些代码

每张脸的正常计算:

Vector3 u = vertices[item.Face1] - vertices[item.Face0]; Vector3 v = vertices[item.Face2] - vertices[item.Face0]; Vector3 fn = new Vector3((uY * vZ) - (uZ * vY), (uZ * vX) - (uX * vZ), (uX * vY) - (uY * vX)); fn.Normalize(); 

在计算顶点周围的每个面的法线之后,我将所有面法线添加到顶点法线并对其进行标准化。 结果显示在图片中,正如您在背景和网格本身中看到的那样,只要没有不同的细分级别,它就会起作用。

一些更多的代码

 ///  /// This is a static indicies array which contains all indicies /// for all possible meshes. ///  private static readonly Int32[] // Subdivision [] // All borders [] Indicies = new Int32[8][][]; // Indicies 

计算当前网格的每个法线:

 Int32 count = 0; for (int y = 0; y < length; y++) { for (int x = 0; x < length; x++) { ns[count++] = GetNormal(x, y, faces, vs); } } 

GetNormal -method:

 private unsafe Vector3 GetNormal(Int32 x, Int32 y, Int32[] indicies, Vector3* vertices) { Vector3 normal = new Vector3(); CalculateNormal(x, y, indicies, vertices, ref normal); normal.Normalize(); // Calculate all face normals and normalize return normal; } 

CalculateNormal -method:

 private unsafe void CalculateNormal(Int32 x, Int32 y, Int32[] indicies, Vector3* vertices, ref Vector3 normal) { Int32 p = ((y * Length) + x); Int32 length = Length - 1; foreach (Face item in FindFaces(this, indicies, p)) { Vector3 u = vertices[item.Face1] - vertices[item.Face0]; Vector3 v = vertices[item.Face2] - vertices[item.Face0]; Vector3 fn = new Vector3((uY * vZ) - (uZ * vY), (uZ * vX) - (uX * vZ), (uX * vY) - (uY * vX)); fn.Normalize(); normal += fn; } SegmentHeighmap heightmap; if (x == 0 && y == 0) { foreach (Face item in FindFaces(Neighbor.Left, out heightmap, TranslateLeftX, TranslateLeftY, x, y)) { Face f = item; AddFaceNormal(ref f, ref normal, heightmap); } ... /* A lot of more code here for each possible combination */ 

AddFaceNormal -method:

 private static void AddFaceNormal(ref Face face, ref Vector3 normal, SegmentHeighmap heightmap) { Vector3 v0; Vector3 v1; Vector3 v2; heightmap.CalculateVertex(face.Face0, out v0); heightmap.CalculateVertex(face.Face1, out v1); heightmap.CalculateVertex(face.Face2, out v2); Vector3 u = v1 - v0; Vector3 v = v2 - v0; Vector3 fn = new Vector3((uY * vZ) - (uZ * vY), (uZ * vX) - (uX * vZ), (uX * vY) - (uY * vX)); fn.Normalize(); normal += fn; } 

FindFaces -methods:

 private IEnumerable FindFaces(Neighbor neighbor, out SegmentHeighmap heightmap, TranslationHandler translateX, TranslationHandler translateY, Int32 x, Int32 y) { Segment segment = Segment.GetNeighbor(neighbor); if (segment != null) { heightmap = segment.Heighmap; Int32 point = ((translateY(this, heightmap, y) * Length) + translateX(this, heightmap, x)); return FindFaces(heightmap, null, point); } heightmap = null; return Enumerable.Empty(); } private IEnumerable FindFaces(SegmentHeighmap heightmap, Int32[] indicies, Int32 point) { indicies = indicies ?? Indicies[heightmap.Segment.SubdivisionLevel][heightmap.SideFlag]; for (int i = 0; i < indicies.Length; i += 3) { Int32 a = indicies[i], b = indicies[i + 1], c = indicies[i + 2]; if (a == point || b == point || c == point) { yield return new Face(a, b, c); } } } 

TransformPoint方法:

 private Int32 TranslatePoint(Int32 point, Segment segment) { Int32 subdiv = segment.SubdivisionLevel - Parent.SubdivisionLevel; if (subdiv == 0) { return point; } if (Math.Abs(subdiv) == 1) { if (subdiv > 0) { return point * 2; } return point / 2; } throw new InvalidOperationException("Subdivision difference is greater than 1"); } 

最后是TranslationHandler -delegate和2个样本处理程序:

 ///  /// Handles the translation from one coordinate space into another /// This handler is used internal only ///  private delegate Int32 TranslationHandler(SegmentHeighmap @this, SegmentHeighmap other, Int32 v); private static readonly TranslationHandler TranslateLeftX = (t, o, v) => o.Length - 1; private static readonly TranslationHandler TranslateLeftY = (t, o, v) => t.TranslatePoint(v, o.Segment); 

问题很简单 :为什么它不适用于不同的级别,我是否会错过计算中的某些内容?

我仍然不知道为什么会发生这种情况,但在更换时……

 Vector3 u = vertices[item.Face1] - vertices[item.Face0]; Vector3 v = vertices[item.Face2] - vertices[item.Face0]; Vector3 fn = new Vector3((uY * vZ) - (uZ * vY), (uZ * vX) - (uX * vZ), (uX * vY) - (uY * vX)); fn.Normalize(); normal += fn; 

与另一个AddFaceNormal -method一起工作。 我不知道为什么会这样,但现在它有效。