it-swarm.com.de

Wie berechnet man Tangens und Binormal?

Apropos Bump-Mapping, Glanzlichter und solche Dinge in OpenGL Shading Language (GLSL)

Ich habe:

  • Ein Feld von Scheitelpunkten (z. B. {0,2,0,5,0,1, 0,2,0,4,0,5, ...})
  • Ein Array von Normalen (z. B. {0.0.0.0.1.0, 0.0,1.0.0.0, ...})
  • Die Position eines Punktlichts im Weltraum (z. B. {0.0,1.0, -5.0})
  • Die Position des Betrachters im Weltraum (z. B. {0.0.0.0.0.0}) (vorausgesetzt, der Betrachter befindet sich im Zentrum der Welt)

Wie kann ich nun den Binormal und den Tangens für jeden Scheitelpunkt berechnen? Ich meine, wie lautet die Formel zum Berechnen der Binormals, was muss ich basierend auf diesen Informationen verwenden? Und über die Tangente?

Ich werde die TBN-Matrix trotzdem konstruieren. Wenn Sie also eine Formel kennen, um die Matrix direkt auf der Grundlage dieser Informationen zu erstellen, wird es Nizza sein!

Oh, ja, ich habe auch die Texturkoordinaten, falls nötig ... ___ Und da ich über GLSL spreche, wäre Nice eine Per-Vertex-Lösung. Ich meine, eine, die nicht auf mehr als einen Scheitelpunkt zugreifen muss Informationen zu einem Zeitpunkt.

---- Update -----

Ich habe diese Lösung gefunden:

 vec3 Tangens; 
 vec3 binormal; 

 vec3 c1 = Kreuz (a_normal, vec3 (0,0, 0,0, 1,0)); 
 vec3 c2 = Kreuz (a_normal, vec3 (0,0, 1,0.) , 0.0)); 

 If (Länge (c1)> Länge (c2)) 
 {
 tangent = c1; 
} 
 sonst 
 {
 tangent = c2; 
} 

 tangent = normalize (tangent); 

 binormal = cross (v_nglNormal, tangent); 
 binormal = normalize (binormal); 

Aber ich weiß nicht, ob es 100% korrekt ist.

43
user464230

Die relevanten Eingabedaten für Ihr Problem sind die Texturkoordinaten. Tangente und Binormal sind Vektoren, die lokal parallel zur Oberfläche des Objekts liegen. Und im Falle eines normalen Mappings beschreiben sie die lokale Orientierung der normalen Textur.

Sie müssen also die Richtung (im Raum des Modells) berechnen, in die die Texturierungsvektoren zeigen. Angenommen, Sie haben ein Dreieck ABC mit Texturkoordinaten HKL. Das gibt uns Vektoren:

D = B-A
E = C-A

F = K-H
G = L-H

Nun wollen wir D und E als Tangentenraum T, U ausdrücken, d.h.

D = F.s * T + F.t * U
E = G.s * T + G.t * U

Dies ist ein System von linearen Gleichungen mit 6 Unbekannten und 6 Gleichungen, die als geschrieben werden können

| D.x D.y D.z |   | F.s F.t | | T.x T.y T.z |
|             | = |         | |             |
| E.x E.y E.z |   | G.s G.t | | U.x U.y U.z |

Invertierung der FG-Matrix-Erträge

| T.x T.y T.z |           1         |  G.t  -F.t | | D.x D.y D.z |
|             | = ----------------- |            | |             |
| U.x U.y U.z |   F.s G.t - F.t G.s | -G.s   F.s | | E.x E.y E.z |

Zusammen mit dem Scheitelpunkt bilden Normal T und U eine lokale Raumbasis, den Tangentenraum, die von der Matrix beschrieben wird

| T.x U.x N.x |
| T.y U.y N.y |
| T.z U.z N.z |

Verwandlung vom tangentialen Raum in den Objektraum. Um Beleuchtungsberechnungen durchzuführen, braucht man das Gegenteil davon. Mit etwas Übung findet man:

T' = T - (N·T) N
U' = U - (N·U) N - (T'·U) T'

Normalisieren wir die Vektoren T 'und U' und nennen sie Tangente und Binormal. Wir erhalten die Matrix, die vom Objekt in den Tangentenraum übergeht, wo wir die Beleuchtung ausführen:

| T'.x T'.y T'.z |
| U'.x U'.y U'.z |
| N.x  N.y  N.z  |

Wir speichern T 'und U' zusammen mit der Vertexnormalen als Teil der Geometrie des Modells (als Vertexattribute), damit wir sie im Shader für Beleuchtungsberechnungen verwenden können. Ich wiederhole: Du bestimmst nicht Tangente und Binormal im Shader, du berechnest sie vor und speichert sie als Teil der Geometrie des Modells (genau wie Normalen).

(Die Notation zwischen den vertikalen Strichen oben sind alle Matrizen, niemals Determinanten, die normalerweise vertikale Striche anstelle von Klammern in ihrer Notation verwenden.)

37
datenwolf

Im Allgemeinen haben Sie zwei Möglichkeiten, die TBN-Matrix zu generieren: offline und online.

  • Online = direkt im Fragment-Shader mit abgeleiteten Anweisungen. Diese Ableitungen geben Ihnen für jeden Punkt eines Polygons eine flache TBN-Basis. Um eine glatte zu erhalten, müssen wir sie basierend auf einer gegebenen (glatten) Vertexnormalen erneut orthogonalisieren. Dieses Verfahren ist auf GPU noch schwerer als die anfängliche TBN-Extraktion.

    // compute derivations of the world position
    vec3 p_dx = dFdx(pw_i);
    vec3 p_dy = dFdy(pw_i);
    // compute derivations of the texture coordinate
    vec2 tc_dx = dFdx(tc_i);
    vec2 tc_dy = dFdy(tc_i);
    // compute initial tangent and bi-tangent
    vec3 t = normalize( tc_dy.y * p_dx - tc_dx.y * p_dy );
    vec3 b = normalize( tc_dy.x * p_dx - tc_dx.x * p_dy ); // sign inversion
    // get new tangent from a given mesh normal
    vec3 n = normalize(n_obj_i);
    vec3 x = cross(n, t);
    t = cross(x, n);
    t = normalize(t);
    // get updated bi-tangent
    x = cross(b, n);
    b = cross(n, x);
    b = normalize(b);
    mat3 tbn = mat3(t, b, n);
    
  • Offline = Tangente als Scheitelpunktattribut vorbereiten. Dies ist schwieriger zu bekommen, da es nicht nur ein weiteres Scheitelattribut hinzufügt, sondern auch alle anderen Attribute neu zusammenstellen müssen. Darüber hinaus wird es nicht zu 100% zu einer besseren Leistung führen, da zusätzliche Kosten für das Speichern/Passieren/Animieren (!) Des vector3-Vertex-Attributs anfallen.

Die Mathematik wird an vielen Stellen beschrieben (google it), einschließlich des @ datenwolf-Posts.

Das Problem hierbei ist, dass 2 Scheitelpunkte dieselbe Normal- und Texturkoordinate haben, jedoch unterschiedliche Tangenten. Das heißt, Sie können einem Knoten nicht nur ein Scheitelpunktattribut hinzufügen, sondern den Scheitelpunkt in zwei Teile aufteilen und unterschiedliche Tangenten für die Klone angeben.

Der beste Weg, um einen eindeutigen Tangens (und andere Attribute) pro Knoten zu erhalten, besteht darin, dies so früh wie möglich zu tun = im Exporter. Auf der Bühne der Sortierung reiner Scheitelpunkte nach Attributen müssen Sie lediglich den Tangentenvektor zum Sortierschlüssel hinzufügen.

Als radikale Lösung des Problems sollten quaternions verwendet werden. Eine einzelne Quaternion (vec4) kann den tangentialen Raum einer vordefinierten Handlichkeit erfolgreich darstellen. Es ist einfach, orthonormal zu bleiben (einschließlich Weitergabe an den Fragment-Shader), zu speichern und bei Bedarf normal zu extrahieren. Weitere Informationen zum KRI-Wiki .

15
kvark

Basierend auf der Antwort von kvark möchte ich noch weitere Gedanken hinzufügen.

Wenn Sie eine orthonormalisierte Tangentenraummatrix benötigen, müssen Sie auf irgendeine Weise etwas tun. Selbst wenn Sie Tangenten- und Binormalattribute hinzufügen, werden diese während der Shader-Phase interpoliert normalisiert noch sind sie normal zueinander.

Nehmen wir an, wir haben einen normalisierten Normalvektorn und wir haben den Tangens t und die binormalb oder wir können sie aus den Ableitungen wie folgt berechnen:

// derivations of the fragment position
vec3 pos_dx = dFdx( fragPos );
vec3 pos_dy = dFdy( fragPos );
// derivations of the texture coordinate
vec2 texC_dx = dFdx( texCoord );
vec2 texC_dy = dFdy( texCoord );
// tangent vector and binormal vector
vec3 t = texC_dy.y * pos_dx - texC_dx.y * pos_dy;
vec3 b = texC_dx.x * pos_dy - texC_dy.x * pos_dx;

Natürlich kann eine orthonormalisierte Tangensraummatrix mit dem Cross-Produkt berechnet werden. Dies funktioniert jedoch nur für Systeme mit der rechten Hand. Wenn eine Matrix gespiegelt wurde (linkes System), wird ein rechtes System verwendet:

t = cross( cross( n, t ), t ); // orthonormalization of the tangent vector
b = cross( n, t );             // orthonormalization of the binormal vector 
                               //   may invert the binormal vector
mat3 tbn = mat3( normalize(t), normalize(b), n );

Im Code-Snippet darüber wird der binormale Vektor umgekehrt, wenn der tangentiale Raum ein linkshändiges System ist .. Um dies zu vermeiden, muss der harte Weg weg sein:

t = cross( cross( n, t ), t ); // orthonormalization of the tangent vector
b = cross( b, cross( b, n ) ); // orthonormalization of the binormal vectors to the normal vector 
b = cross( cross( t, b ), t ); // orthonormalization of the binormal vectors to the tangent vector
mat3 tbn = mat3( normalize(t), normalize(b), n );

Eine gängige Methode zum Orthogonalisieren einer Matrix ist der Gram-Schmidt-Prozess :

t = t - n * dot( t, n ); // orthonormalization ot the tangent vectors
b = b - n * dot( b, n ); // orthonormalization of the binormal vectors to the normal vector 
b = b - t * dot( b, t ); // orthonormalization of the binormal vectors to the tangent vector
mat3 tbn = mat3( normalize(t), normalize(b), n );

Eine andere Möglichkeit besteht darin, die Determinante der 2 * 2-Matrix zu verwenden, die sich aus den Ableitungen der Texturkoordinaten texC_dx, texC_dy ergibt, um die Richtung des binormalen Vektors zu berücksichtigen. Die Idee ist, dass die Determinante einer orthogonalen Matrix 1 ist und die bestimmte einer orthogonalen Spiegelmatrix -1.

Die Determinante kann entweder durch die GLSL-Funktion determinant( mat2( texC_dx, texC_dy ) Oder durch die Formel texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y berechnet werden.

Für die Berechnung der orthonormalisierten Tangentenraummatrix ist der Binormalvektor nicht mehr erforderlich, und die Berechnung des Einheitsvektors .__ (normalize) des Binormalvektors kann umgangen werden.

float texDet = texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y;
vec3 t = texC_dy.y * pos_dx - texC_dx.y * pos_dy;
t      = normalize( t - n * dot( t, n ) );
vec3 b = cross( n, t );                      // b is normlized because n and t are orthonormalized unit vectors
mat3 tbn = mat3( t, sign( texDet ) * b, n ); // take in account the direction of the binormal vector
0
Rabbid76