Calculando normais entre 2 malhas terminando em costuras

Minha taref

Atualmente, estou criando um terreno para o Unity3D, especializado em dispositivos móveis com pouca memória para um aplicativo em execução. Permite um terreno com um tamanho de 15.000 x 15.000 quilômetros e uma altura de -1.000 metros a 10.000 metros, e seus únicos limites são o espaço no disco rígid

Situaçã

Tudo está funcionando bem agora, exceto que as normais entre malhas diferentes (cada malha tem um nível de subdivisão) não são calculadas corretamente. Aqui estão duas fotos que visualizam o problema:

O problema ocorre apenas na transição de um nível de subdivisão para outro. Se as duas malhas tiverem o mesmo nível, funcionará bem. Primeiro, pensei em perder alguns rostos ao calcular os normais, mas parece que todos estão incluídos no cálcul

Some Code

Cálculo normal de cada 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();

Depois de calcular o normal de cada face em torno de um vértice, adiciono todos os normais de face ao normal de vértice e normalizo. O resultado é mostrado nas figuras e, como você pode ver no fundo e nas próprias malhas, funciona desde que não haja um nível de subdivisão diferent

Algo mais código
/// <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

Calcular cada normal da malha atual:

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

OGetNormal-método

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

OCalculateNormal-método

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 */

OAddFaceNormal-método

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

OFindFaces-métodos

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

OTransformPoint-método

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

E finalmente oTranslationHandler -delegate e 2 manipuladores de amostra:

/// <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);
Questã

A pergunta é simples: Por que não está funcionando para níveis diferentes, sinto falta de algo no meu cálculo?

questionAnswers(2)

yourAnswerToTheQuestion