使用行主矩阵时奇怪的特征矩阵乘法行为

时间:2017-08-31 05:55:43

标签: c++ eigen matrix-multiplication row-major-order

以下对我来说很合适:

using Mat4 = Eigen::Matrix4f;
using Vec3 = Eigen::Vector3f;

class Camera {
public:
  Vec3 pos = { 0.0f, 0.0f, 0.0f };
  Vec3 forward = { 0.0f, 0.0f, 1.0f };
  Vec3 up = { 0.0f, 1.0f, 0.0f };
  float vfov = degrees(45.0f);
  float near_plane = 0.1f;
  float far_plane = 100.0f;

  Mat4 cameraToScreen(float aspect_ratio) const {
    Mat4 result;

    float tanHalfFovy = tan(vfov *0.5f);
    float x_scale = 1.0f / (aspect_ratio * tanHalfFovy);
    float y_scale = 1.0f / (tanHalfFovy);

    result << 
      x_scale, 0, 0, 0,
      0, y_scale, 0, 0,
      0, 0, -(far_plane + near_plane) / (far_plane - near_plane), -1.0f,
      0, 0, -2.0f * near_plane * far_plane / (far_plane - near_plane), 0;
    return result;
  }

  Mat4 worldToCamera() const {
    Mat4 result;

    Vec3 p = pos;
    Vec3 f = forward.normalized();
    Vec3 r = f.cross(up).normalized();
    Vec3 u = r.cross(f).normalized();

    result << 
      r.x(), u.x(), -f.x(), 0.0f,
      r.y(), u.y(), -f.y(), 0.0f,
      r.z(), u.z(), -f.z(), 0.0f,
      -r.dot(p), -u.dot(p), f.dot(p), 1.0f;

    return result;
  }

  Mat4 worldToScreen(float aspect_ratio) const {
    return cameraToScreen(aspect_ratio) * worldToCamera();
  }
};

然而,由于这是OpenGL,我不喜欢处理换位,我虽然可以简单地切换到行主矩阵:

using Mat4 = Eigen::Matrix<float, 4, 4, Eigen::RowMajor>;

不幸的是,一切都破了。有趣的是,反转最终的矩阵乘法(这是错误的)解决了这个问题:

Mat4 worldToScreen(float aspect_ratio) const {
  return worldToCamera() * cameraToScreen(aspect_ratio);
}

几乎就像eigen执行矩阵乘法而假设存储是列专业的,无论我的模板标签如何,但这不可能是正确的。

我单独测试了两个矩阵,当它们单独使用时,它们在行主模式下执行,只是两个矩阵的乘积都是错误的。

我不理解Eigen处理行主矩阵的方式?

修改

这是我将数据发送到OpenGL的有效方式。我的代码有很多额外的样板,但归结为:

struct CameraData {
  Mat4 world_to_screen ;
};

void draw(Camera const& cam) {
  CameraData cam_data;

  // I remove the transposed() call when using Eigen::RowMajor
  cam_data.world_to_screen = cam.worldToScreen(16.0f/9.0f).transposed();

  // Camera data has a permanent ubo binding point shared by all programs.
  glBindBuffer(GL_UNIFORM_BUFFER, cam_data_ubo);
  glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(cam_data), &cam_data);

  //.. perform drawing
}

和glsl:

#version 420

in vec3 vertex;

layout (std140) uniform CameraData {
  mat4 world_to_screen;
};

void main() {
  gl_Position = world_to_screen * vec4(vertex, 1.0);
}

0 个答案:

没有答案