Я создаю рендеринг, используя растрирование и глубину буферизации в CPU, и теперь я включил карты нормалей. Все работает, как вы можете видеть на следующем изображении:Касательное пространство для World Space (матрица TBN)
Вопрос заключается в том, что даже то, что он работает, я не понимаю, почему! Реализация противоречит тому, что я думаю. Это код, чтобы получить нормаль в каждом фрагменте:
const Vector3D TexturedMaterial::getNormal(const Triangle3D& triangle_world, const Vector2D& text_coords) const {
Vector3D tangent, bitangent;
calculateTangentSpace(tangent, bitangent, triangle_world);
// Gets the normal from a RGB Texture [0,1] and maps it to [-1, 1]
const Vector3D normal_tangent = (Vector3D) getTextureColor(m_texture_normal, m_texture_normal_width, m_texture_normal_height, text_coords);
const Vector3D normal_world = TangentToWorld(normal_tangent, tangent, bitangent, normal_tangent);
return normal_world;
}
void TexturedMaterial::calculateTangentSpace(Vector3D& tangent, Vector3D& bitangent, const Triangle3D& triangle_world) const {
const Vector3D q1 = triangle_world.v2.position - triangle_world.v1.position;
const Vector3D q2 = triangle_world.v3.position - triangle_world.v2.position;
const double s1 = triangle_world.v2.texture_coords.x - triangle_world.v1.texture_coords.x;
const double s2 = triangle_world.v3.texture_coords.x - triangle_world.v2.texture_coords.x;
const double t1 = triangle_world.v2.texture_coords.y - triangle_world.v1.texture_coords.y;
const double t2 = triangle_world.v3.texture_coords.y - triangle_world.v2.texture_coords.y;
tangent = t2 * q1 - t1 * q2;
bitangent = -s2 * q1 + s1 * q2;
tangent.normalize();
bitangent.normalize();
}
Моя путаница здесь:
const Vector3D TexturedMaterial::TangentToWorld(const Vector3D& v, const Vector3D& tangent, const Vector3D& bitangent, const Vector3D& normal) const {
const int handness = -1; // Left coordinate system
// Vworld = Vtangent * TBN
Vector3D v_world = {
v.x * tangent.x + v.y * bitangent.x + v.z * normal.x,
v.x * tangent.y + v.y * bitangent.y + v.z * normal.y,
v.x * tangent.z + v.y * bitangent.z + v.z * normal.z,
};
// Vworld = Vtangent * TBN(-1) = V * TBN(T)
Vector3D v_world2 = {
v.x * tangent.x + v.y * tangent.y + v.z * tangent.z,
v.x * bitangent.x + v.y * bitangent.y + v.z * bitangent.z,
v.x * normal.x + v.y * normal.y + v.z * normal.z,
};
v_world2.normalize();
// return handness * v_world; --> DOES NOT WORK
return handness * v_world2; --> WORKS
}
Предполагая, что я работаю с векторами строк:
V = (Vx, Vy, Vz)
[Tx Ty Tz]
TBN = [Bx By Bz]
[Nx Ny Nz]
[Tx Bx Nx]
TBN(-1) = [Ty By Ny] // Assume basis are orthogonal TBN(-1) = TBN(T)
[Tz Bz Nz]
Тогда, если T, B и N являются базисными векторами TBN, выраженными в системе координат , трансформации должны быть:
Vworld = Vtangent * TBN
Vtangent = Vworld * TBN(-1)
Но в моем коде я делаю точно противоположное. Чтобы преобразовать нормаль в касательном пространстве к мировому пространству, я умножаюсь на обратную сторону TBN.
Что мне не хватает или непонимания? Предполагается ли, что T, B и N выражены в мировой системе координат неправильно?
Спасибо!
Касательная норма пространства '(0, 0, 1)' будет означать, что этот вектор перпендикулярен поверхности. Умножая это на матрицу TBN, получаем ровно треугольник нормальный (который также перпендикулярен поверхности треугольника). В этом есть смысл. – mtrebi
Матрица должна быть составлена с нормальным треугольником, а не касательной нормалью, потому что касательная нормаль находится в касательном пространстве, а не в мировом пространстве, как касательная и бинагентная. Поэтому касательная нормаль может указывать в направлении, не перпендикулярном касательной и бинагентной. Используя нормальный треугольник, мы знаем, что он находится в мировом пространстве (как касательный и бинагентный), и это перпендикулярно им (по крайней мере, в моем простом случае) и, следовательно, они определяют действительную матрицу преобразования. Я не ошибаюсь? Спасибо, кстати! – mtrebi
Я не уверен, правильно ли понимаю ваш комментарий. Вы перефразировали мой ответ? Вы пробовали - это сработало? –