GLM 中的mat4
mat4中内存的排列方式
mat4的类定义在type_mat4x4.hpp中,下面是对整个类代码的部分截取:
struct tmat4x4
{
typedef tvec4<T, P> col_type;
private:
col_type value[4];
public:
template <typename T, precision P>
GLM_FUNC_QUALIFIER typename tmat4x4<T, P>::col_type & tmat4x4<T, P>::operator[](typename tmat4x4<T, P>::length_type i)
{
assert(i < this->length());
return this->value[i];
}
}
从类代码的实现我们可以看出,mat4中4行4列的元素是按照列的方式排列的,在内存中的排列方式为:
当然我们可以使用[ ]操作符来访问mat4的第几列,并使用二级[ ]来访问某一列的第几个元素,下面是一个简单的给mat4每个元素赋值的例子:
glm::mat4 View;
View[0][0] = 0.0f; View[0][1] = 0.0f; View[0][2] = 0.0f; View[0][3] = 0.0f;
View[1][0] = 0.0f; View[1][1] = 0.0f; View[1][2] = 0.0f; View[1][3] = 0.0f;
View[2][0] = 0.0f; View[2][1] = 0.0f; View[2][2] = 0.0f; View[2][3] = 0.0f;
View[3][0] = 0.0f; View[3][1] = 0.0f; View[3][2] = 0.0f; View[3][3] = 0.0f;
但是为什么glm要把mat4按照列进行内存排列呢?主要是因为opengl在显存中是按列存储的,所以在准备好数据后可以直接将mat4复制到现存中,下面是一个简单的给现存传输mat4的示例代码:
void setMat4(const std::string &name, const glm::mat4 &mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
glm::mat4单位矩阵
glm::mat4 Model = glm::mat4(1.0f);//创建一个单位矩阵
glm::frustum视锥体
glm::mat4 Projection = glm::frustum(-1.234897f*0.1f, 1.234897f*0.1f, -1.234897f*0.1f, 1.2/34897f*0.1f, 0.1f, 1000.0f);
glm::frustum用于生成一个透视矩阵,其中六个入参分别为视锥体的 left, right, bottom, top, nearVal, farVal。
glm四元数转mat4
glm::fquat poseQuat = glm::fquat(qw,qx,qy,qz);
glm::mat4 poseMat = glm::mat4_cast(poseQuat);
代码如上不做详述。
glm::translate
glm::mat4 AA = glm::mat4(1.0f,2.0f,3.0f,4.0f,
5.0f,7.0f,8.0f,9.0f,
4.0f,6.0f,3.0f,2.0f,
9.0f,1.0f,2.0f,3.0f);
LOGCATE("WZJ1029 AA: {%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f}",
AA[0][0],AA[0][1],AA[0][2],AA[0][3],
AA[1][0],AA[1][1],AA[1][2],AA[1][3],
AA[2][0],AA[2][1],AA[2][2],AA[2][3],
AA[3][0],AA[3][1],AA[3][2],AA[3][3]);
glm::mat4 BB = glm::translate(AA, glm::vec3(1.0f, 2.0f, 3.0f));
LOGCATE("WZJ1029 BB: {%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f}",
BB[0][0],BB[0][1],BB[0][2],BB[0][3],
BB[1][0],BB[1][1],BB[1][2],BB[1][3],
BB[2][0],BB[2][1],BB[2][2],BB[2][3],
BB[3][0],BB[3][1],BB[3][2],BB[3][3]);
// AA: {1.000000, 2.000000, 3.000000, 4.000000;
// 5.000000, 7.000000, 8.000000, 9.000000;
// 4.000000, 6.000000, 3.000000, 2.000000;
// 9.000000, 1.000000, 2.000000, 3.000000}
//BB: {1.000000, 2.000000, 3.000000, 4.000000;
// 5.000000, 7.000000, 8.000000, 9.000000;
// 4.000000, 6.000000, 3.000000, 2.000000;
// 32.000000, 35.000000, 30.000000, 31.000000}
首先介绍下运算规则:BB的第一列等于AA第一列,BB的第二列等于AA第二列,BB的第三列等于AA第三列,
BB的第四列 = AA第一列乘以vec3(1) + AA第二列乘以vec3(2) + AA第三列乘以vec3(3) + AA第四列
只看运算规则,并不能很好的理解translate的实际用处,如果考虑如下情景:已知旋转R和位移t组成的转换矩阵
[
R
t
0
1
]
\begin{bmatrix} R & t\\ 0 & 1 \end{bmatrix}
[R0t1],要求逆变换
[
R
−
1
−
R
−
1
t
0
1
]
\begin{bmatrix} R^{-1} & -R^{-1}t\\ 0 & 1 \end{bmatrix}
[R−10−R−1t1],可以用如下代码实现:
glm::fquat poseQuat = glm::fquat(rw,-rx,-ry,-rz);
glm::mat4 poseMat = glm::mat4_cast(poseQuat);
glm::mat4 View = glm::translate(poseMat, glm::vec3(-px,-py,-pz));
glm::mat4的矩阵相乘
LOGCATE("WZJ1029 AA: {%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f}",
AA[0][0],AA[0][1],AA[0][2],AA[0][3],
AA[1][0],AA[1][1],AA[1][2],AA[1][3],
AA[2][0],AA[2][1],AA[2][2],AA[2][3],
AA[3][0],AA[3][1],AA[3][2],AA[3][3]);
LOGCATE("WZJ1029 BB: {%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f}",
BB[0][0],BB[0][1],BB[0][2],BB[0][3],
BB[1][0],BB[1][1],BB[1][2],BB[1][3],
BB[2][0],BB[2][1],BB[2][2],BB[2][3],
BB[3][0],BB[3][1],BB[3][2],BB[3][3]);
glm::mat4 AB = AA * BB;
LOGCATE("WZJ1029 AB: {%f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f; %f, %f, %f, %f}",
AB[0][0],AB[0][1],AB[0][2],AB[0][3],
AB[1][0],AB[1][1],AB[1][2],AB[1][3],
AB[2][0],AB[2][1],AB[2][2],AB[2][3],
AB[3][0],AB[3][1],AB[3][2],AB[3][3]);
//AA: {1.000000, 2.000000, 3.000000, 4.000000;
// 5.000000, 7.000000, 8.000000, 9.000000;
// 4.000000, 6.000000, 3.000000, 2.000000;
// 9.000000, 1.000000, 2.000000, 3.000000}
//BB: {1.000000, 2.000000, 3.000000, 4.000000;
// 5.000000, 7.000000, 8.000000, 9.000000;
// 4.000000, 6.000000, 3.000000, 2.000000;
// 32.000000, 35.000000, 30.000000, 31.000000}
//AB: {59.000000, 38.000000, 36.000000, 40.000000;
// 153.000000, 116.000000, 113.000000, 126.000000;
// 64.000000, 70.000000, 73.000000, 82.000000;
// 606.000000, 520.000000, 528.000000, 596.000000}
glm::mat4矩阵相乘还是行列相乘,唯一需要注意的就是矩阵的排列方式是列存储,最后计算出的结果也是列存储。
glm::scale
glm::rotate
glm::scale,glm::rotate,glm::translate,通常会组合在一起形成对模型坐标系的缩放旋转偏移:
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(0.001f, 0.001f, 0.001f));
Model = glm::rotate(Model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, 0.0f, glm::vec3(0.0f, 1.0f, 0.0f));
Model = glm::translate(Model, -m_pModel->GetAdjustModelPosVec());
glm::mat4 modelMatrix = Model;
glm::mat4 mvpMatrix = Projection * View * Model;
上述代码中GetAdjustModelPosVec用于获取当前模型点云的质心坐标,并将模型移动到世界坐标系的原点位置处。