2009-08-29 53 views
2

爲了在GLSL着色器中使用法線貼圖,您需要知道每個頂點的法線,切線和雙切線向量。 RenderMonkey通過提供它自己的預定義變量(rm_tangentrm_binormal)來實現這一點。我正在嘗試將此功能添加到我自己的3D引擎中。顯然,可以使用每個頂點的xyz座標,uv紋理座標和法向矢量來計算三角形中每個頂點的正切和雙正切。經過一番搜索後,我設計了這個函數來計算我三角形結構中每個頂點的切線和雙切線。計算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); 
} 

當我使用這個功能,將計算得到的值,我的着色器,該機型看起來幾乎就像他們在RenderMonkey中做,但他們在一個非常奇怪的方式閃爍。我將問題追溯到我發送OpenGL的切線和苦行。這讓我懷疑我的代碼做錯了什麼。任何人都可以看到任何問題或有任何其他方法嘗試的建議嗎?

我還應該指出,上面的代碼非常黑客,我對背後的數學理解甚少。

回答

4

找到了解決辦法。更簡單(但仍然有點hacky)代碼:

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

對不起,但這只是錯誤的。你所做的只是將邊緣v01縮放1/u1。 u1不僅可以爲零,而且顯然不正確。要獲得正確的答案,請參閱http://stackoverflow.com/questions/5255806/how-to-calculate-tangent-and-binormal – Gottfried

0

對於u1,u2,v1和v2的某些值,在'r'計算中會得到零除法,導致'r'的未知行爲。你應該防範這一點。找出分母爲零的情況應該是什麼樣的,並且可以解決你的問題。我對這背後的數學知之甚少。

建議執行其設定R = 0,如果分母爲零:

#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

感謝您的答覆。不幸的是,你的建議並沒有幫助。 –