web-dev-qa-db-fra.com

Comment calculer Tangent et Binormal?

Parler du mappage de relief, de la surbrillance spéculaire et de ce genre de choses dans OpenGL Shading Language (GLSL)

J'ai:

  • Un tableau de sommets (par exemple {0.2,0.5,0.1, 0.2,0.4,0.5, ...})
  • Un tableau de normales (par exemple {0.0.0.0.1.0, 0.0,1.0.0.0, ...})
  • La position d’un point lumineux dans l’espace mondial (par exemple {0.0,1.0, -5.0})
  • La position du spectateur dans l'espace mondial (par exemple {0.0,0,0,0,0}) (en supposant que le spectateur se trouve au centre du monde)

Maintenant, comment puis-je calculer le binormal et la tangente pour chaque sommet? Je veux dire, quelle est la formule pour calculer les binormaux, qu'est-ce que je dois utiliser en fonction de ces informations? Et à propos de la tangente?

Je construirai quand même la matrice TBN, donc si vous connaissez une formule pour construire la matrice directement à partir de ces informations, ce sera Nice!

Oh, yeh, j'ai aussi les coordonnées de texture, si besoin . Et comme je parle de GLSL, ce serait bien une solution par sommet, je veux dire, une solution qui n'a pas besoin d'accéder à plus d'un sommet. information à la fois.

---- Mettre à jour -----

J'ai trouvé cette solution:

 vec3 tangente; 
 vec3 binormal; 

 vec3 c1 = croix (anormal, vec3 (0.0, 0.0, 1.0)); 
 vec3 c2 = croix (a_normal, vec3 (0.0, 1.0 , 0.0)); 

 If (longueur (c1)> longueur (c2)) 
 {
 tangente = c1; 
} 
 sinon 
 {
 tangente = c2; 
} 

 tangente = normaliser (tangente); 

 binormal = croix (v_nglNormal, tangente); .__ binormal = normaliser (binormal); 

Mais je ne sais pas si c'est correct à 100%.

43
user464230

Les données d'entrée pertinentes pour votre problème sont les coordonnées de texture. Tangent et Binormal sont des vecteurs localement parallèles à la surface de l'objet. Et dans le cas du mappage normal, ils décrivent l'orientation locale de la texture normale.

Vous devez donc calculer la direction (dans l'espace du modèle) dans laquelle les vecteurs de texturation pointent. Supposons que vous avez un triangle ABC, avec les coordonnées de texture HKL. Cela nous donne des vecteurs:

D = B-A
E = C-A

F = K-H
G = L-H

Nous voulons maintenant exprimer D et E en termes d’espace tangent T, U, c.-à-d.

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

Ceci est un système d'équations linéaires avec 6 inconnues et 6 équations, il peut être écrit comme

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

Inverser le rendement de la matrice FG

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

Ensemble avec le sommet normal T et U forment une base d’espace local, appelée espace tangent, décrite par la matrice

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

Transformer de l'espace tangent en espace objet. Pour faire des calculs d'éclairage, il faut l'inverse de cela. Avec un peu d'exercice on trouve:

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

En normalisant les vecteurs T 'et U', en les appelant tangente et binormale, nous obtenons la matrice transformant l'objet en espace tangent, où nous effectuons l'éclairage:

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

Nous stockons T 'et U' avec le sommet normal en tant que partie intégrante de la géométrie du modèle (en tant qu'attributs de sommet), afin de pouvoir les utiliser dans le shader pour les calculs d'éclairage. Je répète: vous ne déterminez pas les tangentes et les binormales dans le shader, vous les pré-calculez et vous les stockez dans le cadre de la géométrie du modèle (comme des normales).

(Les notations entre les barres verticales ci-dessus sont toutes des matrices, jamais des déterminants, qui utilisent normalement des barres verticales au lieu de crochets dans leur notation.)

37
datenwolf

Généralement, vous avez deux façons de générer la matrice TBN: hors ligne et en ligne.

  • On-line = directement dans le fragment shader en utilisant des instructions dérivées. Ces dérivations vous donnent une base TBN uniforme pour chaque point d'un polygone. Pour obtenir une résolution lisse, nous devons la réorthogonaliser en nous basant sur une normale au sommet donnée (lisse). Cette procédure est encore plus lourde sur le GPU que l'extraction initiale de TBN.

    // 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);
    
  • Off-line = prépare une tangente en tant qu'attribut de sommet. Ceci est plus difficile à obtenir car cela ne va pas seulement ajouter un autre sommet attrib, mais également nécessiter de recomposer tous les autres attributs. De plus, cela ne vous donnera pas à 100% de meilleures performances car vous aurez un coût supplémentaire de stockage/transmission/animation (!) De l'attribut de sommet Vector3.

Le calcul est décrit dans de nombreux endroits (google it), y compris le post @datenwolf.

Le problème ici est que 2 sommets peuvent avoir la même coordonnée normale et texture, mais des tangentes différentes. Cela signifie que vous ne pouvez pas simplement ajouter un attribut de sommet à un sommet, vous devez diviser le sommet en 2 et spécifier des tangentes différentes pour les clones.

Le meilleur moyen d'obtenir une tangente unique (et d'autres attributs) par sommet est de le faire le plus tôt possible = chez l'exportateur. Sur la phase de tri des sommets purs par attributs, il vous suffira d’ajouter le vecteur tangent à la clé de tri.

En tant que solution radicale au problème, envisagez d’utiliser quaternions. Un seul quaternion (vec4) peut représenter avec succès un espace tangentiel d’une maniabilité prédéfinie. Il est facile de garder orthonormé (y compris le passage au fragment shader), de stocker et d'extraire les données normales si nécessaire. Plus d'infos sur le KRI wiki .

15
kvark

Sur la base de la réponse de kvark, je voudrais ajouter plus de pensées.

Si vous avez besoin d'une matrice d'espace tangente orthonormalisée, vous devez travailler d'une manière quelconque . Même si vous ajoutez des attributs tangents et binormaux, ils seront interpolés pendant les étapes du shader À la fin, ils ne sont ni l'un ni l'autre. normalisés ni ils sont normaux les uns aux autres.

Supposons que nous avons un vecteur normal normalisén et que nous avons la tangente t et la binormalb ou nous pouvons les calculer à partir des dérivations comme suit:

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

Bien sûr, une matrice d'espace tangente orthonormalisée peut être calculée à l'aide du produit croisé, , Mais cela ne fonctionnerait que pour les systèmes à main droite. Si une matrice a été mise en miroir (système de gauche), elle passera à un système de droite:

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

Dans l’extrait de code situé au-dessus du vecteur binormal, l’inverse est inversé si l’espace tangent est un système pour gaucher .

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

Un moyen courant d'orthogonaliser toute matrice est le processus Gram – Schmidt :

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

Une autre possibilité consiste à utiliser le déterminant de la matrice 2 * 2, résultant des dérivations des coordonnées de texture texC_dx, texC_dy, pour prendre en compte la direction du vecteur binormal. L'idée est que le déterminant d'une matrice orthogonale est 1 et celui déterminé d'une matrice miroir orthogonale -1.

Le déterminant peut être calculé par la fonction GLSL determinant( mat2( texC_dx, texC_dy ).__ ou par sa formule texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y.

Pour le calcul de la matrice d'espace tangente orthonormalisée, le vecteur binormal n'est plus requis et le calcul du vecteur unitaire .__ (normalize) du vecteur binormal peut être éludé.

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