Расчет касательного трехмерного пространства - PullRequest
2 голосов
/ 29 августа 2009

Чтобы использовать нормальное отображение в шейдерах GLSL, вам необходимо знать нормальный, касательный и битангентный векторы каждой вершины. RenderMonkey облегчает эту задачу, предоставляя для этого собственные предопределенные переменные (rm_tangent и rm_binormal). Я пытаюсь добавить эту функциональность в свой собственный 3D-движок. По-видимому, можно вычислить тангенс и би-тангенс каждой вершины в треугольнике, используя координаты xyz каждой вершины, координаты текстуры UV и вектор нормали. После некоторых поисков я разработал эту функцию, чтобы вычислить касательную и битангенс для каждой вершины в моей структуре треугольника.

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. Это заставляет меня подозревать, что мой код делает что-то не так. Может кто-нибудь увидеть какие-либо проблемы или есть предложения для других методов, чтобы попробовать?

Я также должен отметить, что приведенный выше код очень хакерский, и у меня очень мало понимания математики, стоящей за тем, что происходит.

Ответы [ 2 ]

4 голосов
/ 29 августа 2009

Нашел решение. Гораздо более простой (но все же немного хакерский) код:

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());
}
0 голосов
/ 29 августа 2009

Вы получите нулевое деление в ваших вычислениях 'r' для определенных значений 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;
}
...
...