Расчет нормалей между 2 сетками, заканчивающимися в швах - PullRequest
12 голосов
/ 04 февраля 2012

Моя задача

В настоящее время я создаю ландшафт для Unity3D, который специализируется для мобильных устройств с нехваткой памяти для работающего приложения.Разрешение местности размером 15.000 x 15.000 километров и высотой от -1000 метров до 10.000 метров, и ограничены только пространство на жестком диске.

Ситуация

Все работает нормальнопрямо сейчас, за исключением того, что нормали между разными сетками (каждая сетка имеет уровень подразделения) не рассчитываются правильно.Вот две картинки, которые визуализируют проблему:

Mesh with displayed triangles Mesh with normals only

Проблема возникает только при переходе с одного уровня подразделения на другой.Если оба меша имеют одинаковый уровень, это работает хорошо.Сначала я подумал, что мне не хватает некоторых лиц при вычислении нормалей, но кажется, что все они включены в вычисление.

Какой-то код

Нормальный расчет каждого лица:

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

Вопрос

Вопрос прост : почему он не работает на разных уровнях, я что-то пропускаюв моем расчете?

1 Ответ

1 голос
/ 08 февраля 2012

Я до сих пор не знаю, почему это происходит, но при замене этого ...

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 -методом это работает. Я не знаю, почему это произошло, но теперь это работает.

...