2009-08-29 15 views
2

Pour utiliser le mappage normal dans les shaders GLSL, vous devez connaître les vecteurs normal, tangent et bitangent de chaque sommet. RenderMonkey rend cela facile en fournissant ses propres variables prédéfinies (rm_tangent et rm_binormal) pour cela. J'essaye d'ajouter cette fonctionnalité à mon propre moteur 3d. Apparemment, il est possible de calculer la tangente et la tangente bi de chaque sommet dans un triangle en utilisant les coordonnées xyz de chaque sommet, les coordonnées de texture uv et le vecteur normal. Après quelques recherches, j'ai conçu cette fonction pour calculer la tangente et le bitangent pour chaque sommet de ma structure triangulaire.Calcul de l'espace tangent 3D

void CalculateTangentSpace(void) { 
    float x1 = m_vertices[1]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0); 
    float x2 = m_vertices[2]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0); 
    float y1 = m_vertices[1]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1); 
    float y2 = m_vertices[2]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1); 
    float z1 = m_vertices[1]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2); 
    float z2 = m_vertices[2]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2); 

    float u1 = m_vertices[1]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0); 
    float u2 = m_vertices[2]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0); 
    float v1 = m_vertices[1]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1); 
    float v2 = m_vertices[2]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1); 

    float r = 1.0f/(u1 * v2 - u2 * v1); 

    Vec3<float> udir((v2 * x1 - v1 * x2) * r, (v2 * y1 - v1 * y2) * r, (v2 * z1 - v1 * z2) * r); 
    Vec3<float> vdir((u1 * x2 - u2 * x1) * r, (u1 * y2 - u2 * y1) * r, (u1 * z2 - u2 * z1) * r); 

    Vec3<float> tangent[3]; 
    Vec3<float> tempNormal; 

    tempNormal = *m_vertices[0]->m_normal; 
    tangent[0]=(udir-tempNormal*(Vec3Dot(tempNormal, udir))); 
    m_vertices[0]->m_tangent=&(tangent[0].Normalize()); 
    m_vertices[0]->m_bitangent=Vec3Cross(m_vertices[0]->m_normal, m_vertices[0]->m_tangent); 

    tempNormal = *m_vertices[1]->m_normal; 
    tangent[1]=(udir-tempNormal*(Vec3Dot(tempNormal, udir))); 
    m_vertices[1]->m_tangent=&(tangent[1].Normalize()); 
    m_vertices[1]->m_bitangent=Vec3Cross(m_vertices[1]->m_normal, m_vertices[1]->m_tangent); 

    tempNormal = *m_vertices[2]->m_normal; 
    tangent[2]=(udir-tempNormal*(Vec3Dot(tempNormal, udir))); 
    m_vertices[2]->m_tangent=&(tangent[2].Normalize()); 
    m_vertices[2]->m_bitangent=Vec3Cross(m_vertices[2]->m_normal, m_vertices[2]->m_tangent); 
} 

Lorsque j'utilise cette fonction et envoie les valeurs calculées à mon shaders, les modèles ressemblent presque comme ils le font dans RenderMonkey mais ils flicker dans une manière très étrange. J'ai tracé le problème à la tangente et au bitangent que j'envoie OpenGL. Cela m'amène à penser que mon code fait quelque chose de mal. Quelqu'un peut-il voir des problèmes ou avoir des suggestions pour d'autres méthodes à essayer?

Je devrais également souligner que le code ci-dessus est très hacky et j'ai très peu de compréhension des mathématiques derrière ce qui se passe.

Répondre

4

trouvé la solution. Beaucoup plus simple (mais encore un peu hacky) code:

void CalculateTangentSpace(void) { 
    float x1 = m_vertices[1]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0); 
    float y1 = m_vertices[1]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1); 
    float z1 = m_vertices[1]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2); 

    float u1 = m_vertices[1]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0); 

    Vec3<float> tangent(x1/u1, y1/u1, z1/u1); 
    tangent = tangent.Normalize(); 

    m_vertices[0]->m_tangent = new Vec3<float>(tangent); 
    m_vertices[1]->m_tangent = new Vec3<float>(tangent); 
    m_vertices[2]->m_tangent = new Vec3<float>(tangent); 

    m_vertices[0]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[0]->m_normal, m_vertices[0]->m_tangent)->Normalize()); 
    m_vertices[1]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[1]->m_normal, m_vertices[1]->m_tangent)->Normalize()); 
    m_vertices[2]->m_bitangent=new Vec3<float>(Vec3Cross(m_vertices[2]->m_normal, m_vertices[2]->m_tangent)->Normalize()); 
} 
+2

Désolé, mais c'est faux. Ce que vous faites est simplement l'échelle du bord v01 par 1/u1. Non seulement U1 pourrait être zéro, mais ce n'est évidemment pas correct. Pour une réponse correcte, consultez http://stackoverflow.com/questions/5255806/how-to-calculate-tangent-and-binormal – Gottfried

0

Vous obtiendrez une division nulle dans votre calcul 'r' pour certaines valeurs de u1, u2, v1 et v2 résultant en un comportement inconnu pour 'r'. Vous devriez vous méfier de cela. Déterminez ce que 'r' devrait être si le dénominateur est zéro, et cela pourrait résoudre votre problème. Moi aussi j'ai peu de compréhension des mathématiques derrière cela.

mise en œuvre proposée qui définit r = 0, si le dénominateur est nul:

#include <cmath> 
... 
static float PRECISION = 0.000001f; 
... 
float denominator = (u1 * v2 - u2 * v1); 
float r = 0.f; 
if(fabs(denominator) > PRECISION) {  
    r = 1.0f/denominator; 
} 
... 
+0

Merci pour la réponse. Malheureusement, votre suggestion n'aide pas. –