Cómo calcular tangente y Binormal?

Hablando de mapeo de relieve, resaltado especular y este tipo de cosas en OpenGL Shading Language (GLSL)

Yo tengo:

  • Una matriz de vértices (ej. {0.2,0.5,0.1, 0.2,0.4,0.5, …})
  • Una matriz de normales (por ejemplo, {0.0,0.0,1.0, 0.0,1.0,0.0, …})
  • La posición de un punto de luz en el espacio mundial (por ejemplo, {0.0,1.0, -5.0})
  • La posición del espectador en el espacio mundial (por ejemplo, {0.0,0.0,0.0}) (suponiendo que el espectador se encuentra en el centro del mundo)

Ahora, ¿cómo puedo calcular el Binormal y la Tangente para cada vértice? Quiero decir, ¿cuál es la fórmula para calcular los Binormals, lo que tengo que usar en función de esas informaciones? Y sobre la tangente?

Construiré la Matriz TBN de todos modos, así que si conoces una fórmula para construir la matriz directamente en base a esas informaciones ¡estará bien!

Oh, yeh, también tengo las coordenadas de la textura, si es necesario. Y como estoy hablando de GLSL, sería una buena solución por vértices, es decir, una que no necesita acceder a más de una información de vértice a la vez.

—- Actualización —–

Encontré esta solución:

 tangente vec3;
 vec3 binormal;

 vec3 c1 = cruzado (a_normal, vec3 (0.0, 0.0, 1.0));
 vec3 c2 = cruz (a_normal, vec3 (0.0, 1.0, 0.0));

 if (longitud (c1)> longitud (c2))
 {
     tangente = c1;
 }
 más
 {
     tangente = c2;
 }

 tangente = normalizar (tangente);

 binormal = cross (v_nglNormal, tangente);
 binormal = normalizar (binormal);

Pero no sé si es 100% correcto.

Los datos de entrada relevantes para su problema son las coordenadas de la textura. Tangente y Binormal son vectores localmente paralelos a la superficie del objeto. Y en el caso del mapeo normal, están describiendo la orientación local de la textura normal.

Entonces, debe calcular la dirección (en el espacio del modelo) en la que apuntan los vectores de texturización. Digamos que tienes un triángulo ABC, con coordenadas de textura HKL. Esto nos da vectores:

D = BA E = CA F = KH G = LH 

Ahora queremos express D y E en términos de espacio tangente T, U, es decir,

 D = Fs * T + Ft * U E = Gs * T + Gt * U 

Este es un sistema de ecuaciones lineales con 6 incógnitas y 6 ecuaciones, se puede escribir como

 | Dx Dy Dz | | Fs Ft | | Tx Ty Tz | | | = | | | | | Ex Ey Ez | | Gs Gt | | Ux Uy Uz | 

Invertir los rendimientos de matriz de FG

 | Tx Ty Tz | 1 | Gt -Ft | | Dx Dy Dz | | | = ----------------- | | | | | Ux Uy Uz | Fs Gt - Ft Gs | -Gs Fs | | Ex Ey Ez | 

Junto con el vértice normal T y U forman una base de espacio local, llamada espacio tangente, descrito por la matriz

 | Tx Ux Nx | | Ty Uy Ny | | Tz Uz Nz | 

Transformando desde el espacio tangente al espacio objeto. Para hacer los cálculos de iluminación, uno necesita el inverso de esto. Con un poco de ejercicio uno encuentra:

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

Al normalizar los vectores T ‘y U’, llamándolos tangentes y binormales, obtenemos la matriz que se transforma del objeto al espacio tangente, donde hacemos la iluminación:

 | T'.x T'.y T'.z | | U'.x U'.y U'.z | | Nx Ny Nz | 

Almacenamos T ‘y U’ junto con el vértice normal como parte de la geometría del modelo (como atributos de vértice), de modo que podamos usarlos en el sombreado para cálculos de iluminación. Repito: no determina la tangente y el binormal en el sombreador, los calcula previamente y los almacena como parte de la geometría del modelo (al igual que las normales).

(La notación entre las barras verticales anteriores son todas matrices, nunca determinantes, que normalmente usan barras verticales en lugar de corchetes en su notación).

En general, tiene 2 formas de generar la matriz TBN: fuera de línea y en línea.

  • En línea = a la derecha en el sombreador de fragmentos usando instrucciones derivadas. Esas derivaciones te dan una base plana de TBN para cada punto de un polígono. Para obtener uno sin problemas, tenemos que volver a ortogonalizarlo en función de un vértice normal (suave). Este procedimiento es aún más pesado en GPU que la extracción TBN inicial.

     // 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); 
  • Fuera de línea = preparar la tangente como un atributo de vértice. Esto es más difícil de obtener porque no solo agregará otro vértice attrib sino que también requerirá volver a componer todos los demás atributos. Además, no le dará un 100% de mejor rendimiento ya que obtendrá un costo adicional de almacenamiento / paso / animación (!) Del atributo vector3 vertex.

La matemática se describe en muchos lugares (google it), incluida la publicación @datenwolf.

El problema aquí es que 2 vértices pueden tener la misma coordenada normal y de textura pero diferentes tangentes. Eso significa que no puedes simplemente agregar un atributo de vértice a un vértice, tendrás que dividir el vértice en 2 y especificar diferentes tangentes para los clones.

La mejor forma de obtener tangente única (y otros atributos) por vértice es hacerlo lo antes posible = en el exportador. Allí, en la etapa de clasificación de vértices puros por atributos, solo tendrá que agregar el vector tangente a la clave de clasificación.

Como solución radical al problema, considere usar cuaterniones . Un solo cuaternión (vec4) puede representar con éxito el espacio tangencial de una manejabilidad predefinida. Es fácil de mantener ortonormal (incluido el paso al sombreador de fragmentos), almacenar y extraer normal si es necesario. Más información en la wiki de KRI .

Basado en la respuesta de kvark, me gustaría agregar más pensamientos.

Si necesita una matriz de espacio tangencial ortonormalizada, debe hacer algún trabajo de cualquier forma. Incluso si agrega atributos tangentes y binormales, se interpolarán durante las etapas de sombreado y al final no se normalizarán ni serán normales entre sí.

Supongamos que tenemos un vector normal normalizado n , y tenemos la tangente t y el binormal b o podemos calcularlos a partir de las derivaciones de la siguiente manera:

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

Por supuesto, una matriz de espacio tangente ortormalizada puede calcificarse usando el producto cruzado, pero esto solo funcionaría para los sistemas de la mano derecha. Si se duplicó una matriz (sistema de la izquierda) se convertirá en un sistema de la derecha:

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

En el fragmento de código anterior, el vector binormal se invierte si el espacio de la tangente es un sistema zurdo. Para evitar esto, el camino difícil debe desaparecer:

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

Una forma común de ortogonalizar cualquier matriz es el proceso de 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 ); 

Otra posibilidad es usar el determinante de la matriz 2 * 2, que resulta de las derivaciones de las coordenadas de textura texC_dx , texC_dy , para tomar en cuenta la dirección del vector binormal. La idea es que el determinante de una matriz ortogonal es 1 y el determinado de una matriz espejo ortogonal -1.

El determinante puede ser evaluado por el determinant( mat2( texC_dx, texC_dy ) función GLSL determinant( mat2( texC_dx, texC_dy ) o puede ser calculado por su fórmula texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y .

Para el cálculo de la matriz del espacio tangente ortonormalizado, ya no se requiere el vector binormal y se puede eludir el cálculo del vector unitario ( normalize ) del vector binormal.

 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