计算最终出现接缝的 2 个网格之间的法线

发布于 2025-01-02 05:37:04 字数 5342 浏览 2 评论 0原文

我的任务

我目前为 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((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
fn.Normalize();

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

更多代码

/// <summary>
/// This is a static indicies array which contains all indicies
/// for all possible meshes.
/// </summary>
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-方法:

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-方法:

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((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
        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-方法:

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((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
    fn.Normalize();
    normal += fn;
}

>FindFaces-方法:

private IEnumerable<Face> 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<Face>();
}
private IEnumerable<Face> 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-委托和 2 个示例处理程序:

/// <summary>
/// Handles the translation from one coordinate space into another
/// This handler is used internal only
/// </summary>
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);

问题

问题很简单:为什么它不适用于不同级别,我是否在计算中遗漏了某些内容?

My Task

I currently creating a terrain for Unity3D which is specialized for mobile-devices with low memory for a running app. Allowing a terrain with a size of 15.000 x 15.000 kilometers and a height from -1.000 meters to 10.000 meters and it's only limits are the space on the hard disk.

Situation

Everything is running fine right now except that the normals between different meshes ( each mesh has a subdivision level ) are not calculated correctly. Here are two pictures which visualize the problem:

Mesh with displayed triangles
Mesh with normals only

The problem only occurs on a transition from one subdivision level to another. If both mesh have the same level it works well. I first thought i miss some faces when calculating the normals but it seems they are all included into the calculation.

Some Code

Normal calculation of each face:

Vector3 u = vertices[item.Face1] - vertices[item.Face0];
Vector3 v = vertices[item.Face2] - vertices[item.Face0];

Vector3 fn = new Vector3((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
fn.Normalize();

After calculating the normal of each face around a vertex i add all the face-normals to the vertex-normal and normalize it. The result is shown in the pictures, and as you can see in the background and on the meshes itself it works as long there is no different subdivision level.

Some more code

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

Calculate each normal of the current mesh:

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

The 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;
}

The 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((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
        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 */

The 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((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
    fn.Normalize();
    normal += fn;
}

The FindFaces-methods:

private IEnumerable<Face> 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<Face>();
}
private IEnumerable<Face> 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);
        }
    }
}

The TransformPoint-method:

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");
}

And finally the TranslationHandler-delegate and 2 sample handlers:

/// <summary>
/// Handles the translation from one coordinate space into another
/// This handler is used internal only
/// </summary>
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);

Question

The question is simple: Why is it not working for different levels, do i miss something in my calculation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

水水月牙 2025-01-09 05:37:04

我仍然不知道为什么会发生这种情况,但是当

Vector3 u = vertices[item.Face1] - vertices[item.Face0];
Vector3 v = vertices[item.Face2] - vertices[item.Face0];

Vector3 fn = new Vector3((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
fn.Normalize();
normal += fn;

用另一个 AddFaceNormal 方法替换它时,它会起作用。我不知道为什么会发生这种情况,但现在它有效了。

I still don't know why it happens but when replacing this....

Vector3 u = vertices[item.Face1] - vertices[item.Face0];
Vector3 v = vertices[item.Face2] - vertices[item.Face0];

Vector3 fn = new Vector3((u.Y * v.Z) - (u.Z * v.Y), (u.Z * v.X) - (u.X * v.Z), (u.X * v.Y) - (u.Y * v.X));
fn.Normalize();
normal += fn;

with another AddFaceNormal-method it works. I don't know why this happend but now it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文