Tangent Space mit shared Vertices
-
@dot
Dein Argument mit der Matrizeninversion überzeugt mich und es dürfte tatsächlich deutlich effizienter sein, im Vertexshader alle Lichtquellen sowie den View Vektor in den Tangentspace zu transformieren, anstatt im Pixelshader jeden einzelnene Normalen in den Objectspace.Nun frage ich mich allerdings, wie zum Teufel man den Tangentspace eines beliebigen Meshs berechnen kann? Ich meine, die Normale war ja noch einfach, einfach das Kreuzprodukt zweier beliebiger Seiten des jeweiligen Dreiecks. Aber wie gehts dann weiter, ich meine es gibt ja eine unendlich grosse Anzahl an möglichen Tangent sowie Bitangent vektoren, welche zusammen mit der Normalen einen orthonormalen Vektorraum bilden? Anhand welcher Kriterien kann ich bspw. den Tangentvektor errechnen (der Bitangent ist ja dann wieder nur das Kreuzprodukt zwischen Tangent und Normalen)?
Mfg Samuel
-
Nunja, es gibt natürlich tatsächlich eine unendliche Anzahl an Vektoren die einen Tangentenraum aufspannen. Die Sache ist nur die dass man natürlich nicht irgendeinen beliebigen Tangent Space brauchen kann. Du willst deine Basisvektoren ja so ausgerichtet haben wie die Textur auf dem Mesh liegt. D.h. die x-Achse des Tangentspace soll in die Richtung zeigen in die die Texturkoodinate s läuft und y-Achse in Richtung der Koordinate t und die z-Achse in Richtung der Normale. Denn das ist genau jenes Koordinatensystem in dem sich die Normalen in deiner Normalmap befinden. Eine Herleitung dazu findest du z.B. hier und hier.
Ishildur schrieb:
Dein Argument mit der Matrizeninversion überzeugt mich und es dürfte tatsächlich deutlich effizienter sein, im Vertexshader alle Lichtquellen sowie den View Vektor in den Tangentspace zu transformieren, anstatt im Pixelshader jeden einzelnene Normalen in den Objectspace.
Jap, allerdings kann man das natürlich nicht immer machen. Wenn du z.B. für Reflexions- und Brechunggseffekte entsprechend deiner Normalmap eine Cubemap adressieren willst dann musst du natürlich vom Tangentspace nach draußen gehen...
-
Ich habe noch eine wirklich super Quelle gefunden:
http://jerome.jouvie.free.fr/OpenGl/Lessons/Lesson8.php
-
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?
-
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)*BZwar 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.jpgBeim 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...