世界空间的切线空间(TBN矩阵)

时间:2017-02-09 12:58:32

标签: c++ matrix graphics depth-buffer

我正在使用光栅化和深度缓冲在CPU中构建渲染器,现在我已经包含了法线贴图。一切都像你在下一张图片中看到的那样工作:

enter image description here

问题是,即使它有效,我也不明白为什么!实施是违背我的想法。这是在每个片段中获得正常的代码:

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在世界坐标系中表示错误吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

你的推理是正确的 - 第二个版本是错误的。更直观的方法是分析切线空间法线为(0, 0, 1)时发生的情况。在这种情况下,你显然想要使用三角形法线,这正是第一个版本应该做的。

但是,您输入了错误的参数:

const Vector3D normal_world = TangentToWorld(normal_tangent,
                                             tangent, bitangent, normal_tangent);

最后一个参数需要是三角形法线,而不是从纹理中获取的法线。