在Opengl開發中,運用Eigen與glm數學庫時的心得體會
1, 資料矩陣的儲存區別:
Eigen是按列儲存的, glm是按行儲存的;
glm::mat4 m1(1);
glm::mat4 m0(0);
m1[2][3] = 23;
m1[3][2] = 32;
Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
Result(1,2) = 12;
Result(2,3) = 23;
Result(2,1) = 21;
Result(3,2) = 32;
cout << "Result(1) =\n" << Result << std::endl;
Eigen::Matrix4f Result1 = Eigen::Matrix4f::Zero();
cout << "Result1 =\n" << Result1 << endl;
Eigen::Matrix4f Result2 = Eigen::Matrix4f::Ones();
cout << "Result2 =\n" << Result2 << endl;
2, 資料矩陣的過載*運算子的區別(很重要,不好懂):
glm::vec3 eye(0.0f, 0.0f, 1.0f);
glm::vec3 center(0.0f, 0.0f, 0.0f);
glm::vec3 up(0.0f,1.0f, 0.0f);
glm::mat4 proj = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 100.f);
glm::mat4 view = glm:: glm::lookAt(eye, center, up );
glm::mvp = view*proj;
Camera camera;
Eigen::Vector3f eye(0.0f, 0.0f, 1.0f);
Eigen::Vector3f center(0.0f, 0.0f, 0.0f);
Eigen::Vector3f up(0.0f, 1.0f, 0.0f);
Eigen::Matrix4f proj = camera.perspective(camera.radians(45.0f), 4.0f / 3.0f, 0.1f, 100.f);
Eigen::Matrix4f view = camera.lookAt(eye, center, up);
Eigen::Matrix4f MVP = view*proj;
上面glm中的proj是Eigen中的proj的轉置矩陣,view同理;
3 傳入到shader中的方式不同:
glUniformMatrix4fv(mesh.getShader().uniform("model"), 1, GL_FALSE, &mvp[0][0]); // 行地址
glUniformMatrix4fv(mesh.getShader().uniform("model"), 1, GL_FALSE, MVP.data()); // col address
4, 總結
Eigen符合矩陣運算直覺,適用於廣泛的科學計算,不僅僅三維圖形;glm只適用於opengl空間座標變換,只為opengl而生,即 便如此,也不如Eigen好用。Eigen不足是沒有透視變換,正交變換和相機觀察變換,這個可以自己實現,也可以仿照glm改寫,但要求對矩陣按列與按行儲存方式對矩陣運算的影響以及按列傳資料到時shader有根本認識。
本人仿照glm改寫的透視變換Perspective(...), 正交變換ortho(...)和相機觀察變換lookAt(...)如下:
Eigen::Matrix4f Camera::perspective(float fovy, float aspect, float zNear, float zFar)
{
float const tanHalfFovy = tan(fovy / 2);
Eigen::Matrix4f Result = Eigen::Matrix4f::Zero();
Result(0, 0) = 1 / (aspect * tanHalfFovy);
Result(1, 1) = 1 / (tanHalfFovy);
Result(2, 2) = -(zFar + zNear) / (zFar - zNear);
Result(3, 2) = -1.0;
Result(2, 3) = -(2 * zFar * zNear) / (zFar - zNear);
return Result;
}
Eigen::Matrix4f Camera::ortho(float left, float right, float bottom, float top, float zNear, float zFar)
{
Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
Result(0, 0) = 2 / (right - left);
Result(1, 1) = 2 / (top - bottom);
Result(2, 2) = -2 / (zFar - zNear);
Result(0, 3) = -(right + left) / (right - left);
Result(1, 3) = -(top + bottom) / (top - bottom);
Result(2, 3) = -(zFar + zNear) / (zFar - zNear);
return Result;
}
Eigen::Matrix4f Camera::lookAt(Eigen::Vector3f const& eye, Eigen::Vector3f const& center, Eigen::Vector3f const& up)
{
Eigen::Vector3f f(center - eye);
Eigen::Vector3f s(f.cross(up));
Eigen::Vector3f u(s.cross(f));
f.normalize();
s.normalize();
u.normalize();
Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
Result(0, 0) = s.x();
Result(0, 1) = s.y();
Result(0, 2) = s.z();
Result(1, 0) = u.x();
Result(1, 1) = u.y();
Result(1, 2) = u.z();
Result(2, 0) = -f.x();
Result(2, 1) = -f.y();
Result(2, 2) = -f.z();
Result(0, 3) = -s.dot(eye);
Result(1, 3) = -u.dot(eye);
Result(2, 3) = f.dot(eye);
return Result;
}