Tangent Space mit shared Vertices



  • Mal ne ganz blauäugige Frage...: gibt's dafür nicht nen Standard, der festlegt wie man die Tangente und Binormale zu berechnen hat? Bzw. genau genommen zwei Standards. Man kann ja in den diversen 3D-Programmen (Max, ...) zwischen zwei Methoden auswählen.

    Und wenn es solche Standards gibt, müssten die das Thema dann nicht abdecken?

    Ist ja schliesslich nicht so als ob "Normalen" die nicht rechtwinkelig zum eigentlichen Dreieck stehen besonders selten wären 🙂



  • Was sollte so ein Standard deiner Meinung nach genau spezifizieren?



  • @hustbaer

    Ist ja schliesslich nicht so als ob "Normalen" die nicht rechtwinkelig zum eigentlichen Dreieck stehen besonders selten wären

    😕
    Das kann ich jetzt grad nicht nachvollziehen?
    Oder meinst du einfach bei gemittelten Tangentspaces in Folge Vertexsharing?



  • Nun ich denke er spielt auf Smoothing Groups bzw. eben die Mittelung der Normalen an...



  • @dot
    Ich habe die von dir (und auch mir) verlinkten Resourcen studiert und nun stundenlang mit Papier und Stift durchgespielt. Dabei gibt es einfach zwei Dinge, die ich nicht in meinen Schädel kriege. Eines davon hatte ich bereits in einem früheren Post erwähnt (wobei ich damals nur so einen auf mathematischer Intuition basierenden Verdacht hatte), bei meinen Pen & Paper Versuchen hat sich dies allerdings nun bestätigt.

    1. Die Tangent und Bitangent Vektoren sind in den seltensten Fällen orthogonal, dies lässt sich einfach beweisen, wenn man die Berechnung mit folgender Gleichung ausdrückt:

    P1-P0 = (u1-u0)*T+(v1-v0)*B
    P2-P0 = (u2-u0)*T+(v2-v0)*B

    Zwar schreibt der Autor von "Computing Tangent Space Basis Vectors for an Arbitrary Mesh"

    It is not necesseraly true that the tangent vectors are perpendicular to each other... ...It is safe to assume, however, that the three vectors will at least be close to orthogonal...

    Also da finde ich aber schon Texturkoordinaten (u0,u1,u2,v0,v1,v2), welche dazu führen werden, dass die obige Gleichung nur noch mit T und B Vektoren gelöst werden kann, die alles andere also rechtwinklig zueinander sein werden. Auf diese Weise wird eine Gram-Schmidt Orthogonalisierung einen Vektorraum generieren, der mit dem Tangentspace nicht mehr viel gemein hat :p Oder was denkt Ihr da resp. welche Erfahrungen habt Ihr damit?

    Wenn man die obige Gleichung etwas genauer betrachtet fällt auch auf, dass man Problemlos Werte für (u0,u1,u2,v0,v1,v2) findet, welche dieses Gleichungssystem unlösbar machen, einfachstes Beispiel:

    u1 = u2 and v1 = v2

    Mit anderen Worten, ich setze bei mind. zwei der drei Vertizen eines Dreiecks dieselben Texturkoordinaten, dann ist dieses Gleichungssystem nur noch lösbar, wenn diese beiden Vertizen auch dieselbe Position im Raum haben...

    Liege ich da völlig falsch?



  • dot schrieb:

    Was sollte so ein Standard deiner Meinung nach genau spezifizieren?

    Na wie man aus Vertizes + UV-Koordinaten + Normalen die Tangenten und Binormalen ausrechnet.
    Was sonst 😕



  • hustbaer schrieb:

    Na wie man aus Vertizes + UV-Koordinaten + Normalen die Tangenten und Binormalen ausrechnet.

    Ich seh nicht wirklich wofür man da einen Standard brauchen sollte. Wie man die berechnet geht aus der Definition von Tangente und Binormale hervor!?



  • Also da finde ich aber schon Texturkoordinaten (u0,u1,u2,v0,v1,v2), welche dazu führen werden, dass die obige Gleichung nur noch mit T und B Vektoren gelöst werden kann, die alles andere also rechtwinklig zueinander sein werden.

    Das sind dann aber eben keine sinnvollen Texturkoordinaten.
    Ziel des Mappings ist ja, regelmaessige und verzerrungsfreie Koordinaten zu erzeugen...



  • @hellihjb
    Dennoch wird das Programm abstürzen, nur weil der Grafiker nicht aufgepasst hat :p



  • Warum sollte das Programm abstürzen nur weil die Tangentspace-Basisvektoren falsch sind!?



  • Weil dann die Diskriminante der Gleichungsmatrix 0.0f sein wird, welche ja als Dividend vorkommt. Division by Zero exception 😉



  • dot schrieb:

    hustbaer schrieb:

    Na wie man aus Vertizes + UV-Koordinaten + Normalen die Tangenten und Binormalen ausrechnet.

    Ich seh nicht wirklich wofür man da einen Standard brauchen sollte. Wie man die berechnet geht aus der Definition von Tangente und Binormale hervor!?

    Wenn das so klar wäre, dann gäbe es diesen Thread nicht.



  • Ich habe die Berechnung des Tangentspace nun einigermassen hingekriegt, aber irgendwas stimmt nocht nicht. Hier mal zwei Screenshot:

    http://dl.dropbox.com/u/2095717/Screenshot01.jpg
    http://dl.dropbox.com/u/2095717/Screenshot02.jpg

    Beim zweiten sieht man auf der linken Seite deutlich einen Fehler, die Normalen zeigen nämlich in das Schiff hinein.

    Hier ist der entsprechende Code ausschnitt:

    // otherwise check if the current token is a
      // TRI_FACELIST token (subtoken of OBJ_TRIMESH)
      else if(idTok == 0x4120){
       // determine the number of triangles (faces) and
       // move the datapointer to the first triangle
       uint32 c=*reinterpret_cast<uint16*>(pCur+=6)*3;
       pCur += 2;
    
       // start a loop for walking each triangle (face)
       for(uint32 k=0;k<c;k+=3){
        // copy the 3 indices defining the current face into the temporary array
        // (we don't know the exact position within the index buffer yet since we need to
        // group those indices by material in order to provide the maximum optimization
        // level during realtime rendering, additionally we need to switch two of the indices
        // in order to reverse the backface culling from clockwise to counter clockwise)
        MeshVertex *vt0 = &pVtx[aTmp[k]   = *reinterpret_cast<uint16*>(pCur)];
        MeshVertex *vt1 = &pVtx[aTmp[k+1] = *reinterpret_cast<uint16*>(pCur+4)];
        MeshVertex *vt2 = &pVtx[aTmp[k+2] = *reinterpret_cast<uint16*>(pCur+2)];
    
        // compute the vectors between the corner points of the curren triangle (face)
        Vector3 q1 = vt1->Position-vt0->Position;
        Vector3 q2 = vt2->Position-vt0->Position;
    
        // compute the delta of the texture coordinates for q1 and q2
        float32 s1 = vt1->Texture.X-vt0->Texture.X;
        float32 s2 = vt2->Texture.X-vt0->Texture.X;
        float32 t1 = vt1->Texture.Y-vt0->Texture.Y;
        float32 t2 = vt2->Texture.Y-vt0->Texture.Y;
    
        // compute the tangent space
        Vector3 vcT = Vector3::Normalize((q1*t2-q2*t1)/(s1*t2-s2*t1));
        Vector3 vcB = Vector3::Normalize((q1*s2-q2*s1)/(s2*t1-s1*t2));
        Vector3 vcN = Vector3::Cross(vcT,vcB);
    
        // add the tangent space of this triangle (face) to each vertex
        // contained by this triangle (face). This way we are averaging
        // the computed normal for shared vertices
        vt0->Tangent   += vcT;
        vt0->Bitangent += vcB;
        vt0->Normal    += vcN;
        vt1->Tangent   += vcT;
        vt1->Bitangent += vcB;
        vt1->Normal    += vcN;
        vt2->Tangent   += vcT;
        vt2->Bitangent += vcB;
        vt2->Normal    += vcN;
    
        // advance the data pointer to the start of the next triangle (face)
        // (each such triangle contains another 16-bit value
        // of information which we simply ignore)
        pCur += 4*sizeof(uint16);
       }
      }
    

    Sieht vielleicht jemand, was ich da falsch mache? Wahrscheinlich war es keine so kluge Idee, die Normale als Kreuzprodukt des Tangent u. Bitangent zu berechnen. Allerdings wird es ja in jedem Artikel genauso gemacht. Ich werde mal versuchen, die Normale unabhängig davon als Kreuzprodukt der Dreieckskanten zu berechnen auch wenns halt etwas aufwändiger ist. Gram-Schmidt sorgt dann für den Rest 😉



  • Na wer sagts denn... ...und plötzlich geht es:

    Anstatt:

    // compute the tangent space
     Vector3 vcT = Vector3::Normalize((q1*t2-q2*t1)/(s1*t2-s2*t1));
     Vector3 vcB = Vector3::Normalize((q1*s2-q2*s1)/(s2*t1-s1*t2));
     Vector3 vcN = Vector3::Cross(vcT,vcB);
    

    wechseln zu

    // compute the tangent space
     Vector3 vcT = Vector3::Normalize((q1*t2-q2*t1)/(s1*t2-s2*t1));
     Vector3 vcB = Vector3::Normalize((q1*s2-q2*s1)/(s2*t1-s1*t2));
     Vector3 vcN = Vector3::Cross(q1,q2);
    

    War nicht mal aufwändiger :p

    Ich frage mich manchmal, ob die Leute, die solche Tutorials schreiben, diese eigentlich jemals selbst implementiert haben, oder einfach ein bereits existierendes Tutorial in Ihren eigenen <<schönen>> Worten neu verfassen und als das Ihrige verkaufen. Mir ist jetzt des Öfteren aufgefallen, dass sich die Tutorials doch oft sehr ähnlich sind und erstaunlicherweise über genau dieselben Steine stolpern...


Anmelden zum Antworten