Cómo calcular vectores tangentes y bitangentes

9

Tengo una textura cargada en three.js, luego pasé a los sombreadores. En el sombreador de vértices calculo lo normal y guardo en una variable el vector uv.

<script id="vertexShader" type="x-shader/x-vertex">

                varying vec3 N,P;
                varying vec2 UV;

                void main() {
                    gl_Position= projectionMatrix * modelViewMatrix * vec4(position,1.0);
                    P= position;
                    N= normalMatrix * vec3(normal);
                    UV= uv;
                }
            </script>
            <script id="fragmentShader" type="x-shader/x-fragment">

                varying vec3 N,P;
                varying vec2 UV;
                uniform sampler2D texture;

                void main() {
                    gl_FragColor= texture2D(texture,UV);
                }

            </script>

¿Cómo calculo los vectores T y B?

Ramy Al Zuhouri
fuente
2
¿Desea el algoritmo en general o específicamente para su biblioteca de elección?
concept3d
Si puedo calcularlo con three.js, sería mejor.
Ramy Al Zuhouri

Respuestas:

26

En primer lugar, para cada vértice 3D hay infinitos vectores tangentes y bi-tangentes. La imagen a continuación explica por qué hay un número infinito de espacios tangentes para cada vértice, la tangente y la bitangente pueden tener cualquier dirección en el plano mostrado.

Número infinito de espacios en tangencia para cada vértice

Entonces, para calcular adecuadamente el espacio tangente 1 más útil , queremos que nuestro espacio tangente esté alineado de modo que el eje x (la tangente) corresponda a la dirección u en el mapa de relieve y el eje y (bitangente) corresponda a la dirección v en el mapa de relieve, ya deberíamos tener la normalidad del vértice que ya corresponde a la dirección Z en el espacio tangente.

(1) más útil porque al final queremos que los vectores normales se muestreen a partir de la textura

Eso se explica mejor con imágenes, queremos que nuestro espacio tangente se alinee como se (u, v)muestra a continuación.

ingrese la descripción de la imagen aquí

La fuente de la imagen, aunque no está estrictamente relacionada con los gráficos por computadora

En los gráficos de computadora, los desarrolladores suelen usar (u,v)también conocidos como coordenadas de textura. Asumiremos que T es la tangente y B es la bitangente, y P0es nuestro vértice objetivo, que es parte del triángulo (P0,P1,P2).

Primero recuerde lo que queríamos hacer, es calcular tangente y bitanget que:

  1. T alineado con u y B alineado con v.
  2. T y B se encuentran en el plano con el vértice normal (el plano que se muestra en la imagen de arriba).

El punto es que ya asumimos que T y B se encuentran en el mismo plano y corresponden a U y V ahora si podemos conocer sus valores podemos cruzar el producto y el tercer vector para construir una matriz de transformación del mundo al espacio tangente.

ingrese la descripción de la imagen aquí

Dado que sabemos que cualquier vector 2D puede escribirse como una combinación lineal de dos vectores independientes 2 y dado que ya tenemos los puntos triangulares (bordes), que se muestran en la imagen de arriba. Podemos escribir:

E1 = (u1-u0) T + (v1-v0) B

E2 = (u2-u0) T + (v2-v0) B

(2) en realidad así es como se deriva la matriz base

La ecuación anterior se puede escribir en forma de matriz,

| E1x E1y E1z |   | deltaU1 deltaV1 | * | Tx Ty Tz |
| E2x E2y E2z | = | deltaU2 deltaV2 |   | Bx By Bz |

Al resolver la ecuación de matrices podemos determinar los valores de T y B, podemos construir una matriz de transformación.

El código fuente completo en C ++

#include "Vector4D.h"


struct Triangle
{
    unsigned short  index[3];
};


void CalculateTangentArray(long vertexCount, const Point3D *vertex, const Vector3D *normal,
        const Point2D *texcoord, long triangleCount, const Triangle *triangle, Vector4D *tangent)
{
    Vector3D *tan1 = new Vector3D[vertexCount * 2];
    Vector3D *tan2 = tan1 + vertexCount;
    ZeroMemory(tan1, vertexCount * sizeof(Vector3D) * 2);

    for (long a = 0; a < triangleCount; a++)
    {
        long i1 = triangle->index[0];
        long i2 = triangle->index[1];
        long i3 = triangle->index[2];

        const Point3D& v1 = vertex[i1];
        const Point3D& v2 = vertex[i2];
        const Point3D& v3 = vertex[i3];

        const Point2D& w1 = texcoord[i1];
        const Point2D& w2 = texcoord[i2];
        const Point2D& w3 = texcoord[i3];

        float x1 = v2.x - v1.x;
        float x2 = v3.x - v1.x;
        float y1 = v2.y - v1.y;
        float y2 = v3.y - v1.y;
        float z1 = v2.z - v1.z;
        float z2 = v3.z - v1.z;

        float s1 = w2.x - w1.x;
        float s2 = w3.x - w1.x;
        float t1 = w2.y - w1.y;
        float t2 = w3.y - w1.y;

        float r = 1.0F / (s1 * t2 - s2 * t1);
        Vector3D sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                (t2 * z1 - t1 * z2) * r);
        Vector3D tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                (s1 * z2 - s2 * z1) * r);

        tan1[i1] += sdir;
        tan1[i2] += sdir;
        tan1[i3] += sdir;

        tan2[i1] += tdir;
        tan2[i2] += tdir;
        tan2[i3] += tdir;

        triangle++;
    }

    for (long a = 0; a < vertexCount; a++)
    {
        const Vector3D& n = normal[a];
        const Vector3D& t = tan1[a];

        // Gram-Schmidt orthogonalize
        tangent[a] = (t - n * Dot(n, t)).Normalize();

        // Calculate handedness
        tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F;
    }

    delete[] tan1;
}

El código fuente completo y la derivación se pueden encontrar aquí .

concepto3d
fuente
¿Qué pasa si no tengo un triángulo? En mi caso tengo una textura que debería aplicarse en una esfera. ¿Cómo adaptarlo a este caso?
Ramy Al Zuhouri
@RamyAlZuhouri ¿no es la esfera construida a partir de triángulos? Simplemente recorre los vértices como en el código. Si su esfera no está basada en triángulos, esa es una historia totalmente diferente.
concept3d
Estoy usando three.js SphereGeometry (en javascript). ¿Tal vez debería pasar la propiedad de la cara a los sombreadores? La esfera que dibujo tiene 1089 vértices y 1084 caras.
Ramy Al Zuhouri
1
calcula el espacio tangente y luego pasa las tangentes a los sombreadores. Y debe tener acceso a las caras / vértices para calcular el espacio tangente.
concept3d
En mi caso tendré 1084 tangentes, ¿cómo mapeo las tangentes con los vértices?
Ramy Al Zuhouri