Comment calculer des vecteurs tangents et bitangents

9

J'ai une texture chargée dans three.js, puis passée aux shaders. Dans le vertex shader, je calcule la normale et j'enregistre dans une variable le vecteur 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>

Comment calculer les vecteurs T et B?

Ramy Al Zuhouri
la source
2
voulez-vous l'algorithme en général ou spécifiquement pour votre bibliothèque de choix?
concept3d
Si je peux le calculer avec three.js, ce serait mieux.
Ramy Al Zuhouri

Réponses:

26

Tout d'abord, pour chaque sommet 3D, il existe des vecteurs tangents et bi-tangents infinis. L'image ci-dessous explique pourquoi il y a un nombre infini d'espaces tangents pour chaque sommet, la tangente et la bitangente peuvent avoir n'importe quelle direction dans le plan indiqué.

Nombre infini d'espaces de tanget pour chaque sommet

Donc, afin de calculer correctement l' espace tangent 1 le plus utile , nous voulons que notre espace tangent soit aligné de sorte que l'axe x (la tangente) corresponde à la direction u dans la carte de relief et que l'axe y (bitangent) corresponde à la direction v dans la bump map, nous devrions déjà avoir une normale du sommet qui correspond déjà à la direction Z dans l'espace tangent.

(1) plus utile parce qu'au final, nous voulons que des vecteurs normaux soient échantillonnés à partir de la texture

Cela doit être expliqué avec des images, nous voulons que notre espace tangent soit aligné comme (u, v)indiqué ci-dessous.

entrez la description de l'image ici

Source de l'image mais pas strictement liée à l'infographie

En infographie, les développeurs utilisent généralement (u,v)également les coordonnées de texture. Nous supposerons que T est la tangente et B est la bitangente, et P0est notre sommet cible, qui fait partie du triangle (P0,P1,P2).

Rappelez-vous d'abord ce que nous voulions faire, c'est de calculer la tangente et le bitanget qui:

  1. T aligné avec u et B aligné avec v.
  2. T et B se trouvent dans le plan avec le sommet normal (le plan montré dans l'image ci-dessus).

Le point est que nous avons déjà supposé que T et B se trouvent dans le même plan et correspondent à U et V maintenant si nous pouvons connaître leurs valeurs, nous pouvons croiser le produit et le troisième vecteur pour construire une matrice de transformation du monde à l'espace tangent.

entrez la description de l'image ici

Étant donné que nous savons que tout vecteur 2D peut être écrit comme une combinaison linéaire de deux vecteurs indépendants 2 et que nous avons déjà les points triangulaires (bords), illustrés dans l'image ci-dessus. Nous pouvons écrire:

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

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

(2) En fait, c'est ainsi que la matrice de base est dérivée

L'équation ci-dessus peut être écrite sous forme matricielle,

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

En résolvant l'équation des matrices, nous pouvons déterminer les valeurs T et B, nous pouvons construire une matrice de transformation.

Le code source complet 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;
}

Le code source complet et la dérivation peuvent être trouvés ici .

concept3d
la source
Et si je n'ai pas de triangle? Dans mon cas, j'ai une texture qui devrait être appliquée sur une sphère. Comment l'adapter à ce cas?
Ramy Al Zuhouri
@RamyAlZuhouri n'est pas la sphère construite à partir de triangles? Vous venez de boucler sur les sommets comme dans le code. Si votre sphère n'est pas basée sur un triangle, c'est une histoire totalement différente.
concept3d
J'utilise three.js SphereGeometry (en javascript). Peut-être que je devrais transmettre la propriété face aux shaders? La sphère que je dessine a 1089 sommets et 1084 faces.
Ramy Al Zuhouri
1
vous calculez l'espace tangent puis passez les tangentes aux shaders. Et vous devriez avoir accès aux faces / sommets afin de calculer l'espace tangent.
concept3d
Dans mon cas, j'aurai 1084 tangentes, comment mapper les tangentes avec les sommets?
Ramy Al Zuhouri