上节主要详解了 GLM 库的两个数据结构 vec 和 mat。这节主要是详解vec 和 mat 的一些基本操作。这里主要看通过重载运算符实现的一些操作。
vec
上节说过对[]的重载,主要用于访问vec的分量。这节讨论对运算符的重载,分为两类,一类是赋值运算符,比如+=、-=等,另一类是算术运算符、关系运算符、逻辑运算符和位运算符等。 先讨论赋值运算符:
template<typename T, qualifier Q>
template<typename U>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(U scalar)
{
this->x += static_cast<T>(scalar);
this->y += static_cast<T>(scalar);
this->z += static_cast<T>(scalar);
return *this;
}
template<typename T, qualifier Q>
template<typename U>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<1, U, Q> const& v)
{
this->x += static_cast<T>(v.x);
this->y += static_cast<T>(v.x);
this->z += static_cast<T>(v.x);
return *this;
}
template<typename T, qualifier Q>
template<typename U>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<3, U, Q> const& v)
{
this->x += static_cast<T>(v.x);
this->y += static_cast<T>(v.y);
this->z += static_cast<T>(v.z);
return *this;
}
因此除了=之外的其他赋值运算符都可以在右边放三种参数:
vec3 a(1.2, 3.6, 4.8);
vec1 b(1.2);
a /= b;
cout << a.x << " " << a.y << " " << a.z << endl; //1 3 4
vec3 c(1.2, 3.6, 4.8);
vec3 d(1.2, 3.6, 4.8);
c /= d;
cout << c.x << " " << c.y << " " << c.z << endl; //1 1 1
vec3 e(1.2, 3.6, 4.8);
float f = 4.8;
e /= f;
cout << e.x << " " << e.y << " " << e.z << endl; //0.25 0.75 1
除了赋值运算符,还有其他运算符重载,以乘法运算为例:
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar)
{
return vec<3, T, Q>(
v.x * scalar,
v.y * scalar,
v.z * scalar);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar)
{
return vec<3, T, Q>(
v.x * scalar.x,
v.y * scalar.x,
v.z * scalar.x);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v)
{
return vec<3, T, Q>(
scalar * v.x,
scalar * v.y,
scalar * v.z);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v)
{
return vec<3, T, Q>(
scalar.x * v.x,
scalar.x * v.y,
scalar.x * v.z);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2)
{
return vec<3, T, Q>(
v1.x * v2.x,
v1.y * v2.y,
v1.z * v2.z);
}
那么乘法操作可以这样使用:
vec3 a(1.2, 3.6, 4.8);
float b = 2.2;
vec1 c(3.2);
vec3 d(2.2, 1, 1);
vec3 res1 = a * b;
cout << res1.x << " " << res1.y << " " << res1.z << endl; //2.64 7.92 10.56
vec3 res2 = a * c;
cout << res2.x << " " << res2.y << " " << res2.z << endl; //3.84 11.52 15.36
vec3 res3 = b * a;
cout << res3.x << " " << res3.y << " " << res3.z << endl; //2.64 7.92 10.56
vec3 res4 = c * a;
cout << res4.x << " " << res4.y << " " << res4.z << endl; //3.84 11.52 15.36
vec3 res5 = a * d;
cout << res5.x << " " << res5.y << " " << res5.z << endl; //2.64 3.6 4.8
除了乘法运算符而言,其他运算符基本都支持这五种形式。
mat
对于 mat,除了矩阵与常量、矩阵的加减法之外,还定义了多种乘法和除法,以 mat2x3矩阵为例,具体定义如下:
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar)
{
return mat<2, 3, T, Q>(
m[0] * scalar,
m[1] * scalar);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m)
{
return mat<2, 3, T, Q>(
m[0] * scalar,
m[1] * scalar);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER typename mat<2, 3, T, Q>::col_type operator*
(
mat<2, 3, T, Q> const& m,
typename mat<2, 3, T, Q>::row_type const& v)
{
return typename mat<2, 3, T, Q>::col_type(
m[0][0] * v.x + m[1][0] * v.y,
m[0][1] * v.x + m[1][1] * v.y,
m[0][2] * v.x + m[1][2] * v.y);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER typename mat<2, 3, T, Q>::row_type operator*
(
typename mat<2, 3, T, Q>::col_type const& v,
mat<2, 3, T, Q> const& m)
{
return typename mat<2, 3, T, Q>::row_type(
v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2],
v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2]);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2)
{
return mat<2, 3, T, Q>(
m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1]);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2)
{
T SrcA00 = m1[0][0];
T SrcA01 = m1[0][1];
T SrcA02 = m1[0][2];
T SrcA10 = m1[1][0];
T SrcA11 = m1[1][1];
T SrcA12 = m1[1][2];
T SrcB00 = m2[0][0];
T SrcB01 = m2[0][1];
T SrcB10 = m2[1][0];
T SrcB11 = m2[1][1];
T SrcB20 = m2[2][0];
T SrcB21 = m2[2][1];
mat<3, 3, T, Q> Result;
Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01;
Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01;
Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01;
Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11;
Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11;
Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11;
Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21;
Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21;
Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21;
return Result;
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2)
{
return mat<4, 3, T, Q>(
m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1],
m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1],
m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1],
m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1],
m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1],
m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1],
m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1],
m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1],
m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1],
m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1],
m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1],
m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1]);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar)
{
return mat<2, 3, T, Q>(
m[0] / scalar,
m[1] / scalar);
}
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m)
{
return mat<2, 3, T, Q>(
scalar / m[0],
scalar / m[1]);
}
因此在使用的时候可以按照以下方式使用:
mat2x3 a( vec3(2, 2, 2), vec3(2, 2, 2));
float b = 1.1;
mat2x3 c = a * b;
cout << c[0][0] << " " << c[0][1] << " " << c[0][2] << endl;
cout << c[1][0] << " " << c[1][1] << " " << c[1][2] << endl;
// 2.2 2.2 2.2
// 2.2 2.2 2.2
mat2x3 d = b* a;
cout << d[0][0] << " " << d[0][1] << " " << d[0][2] << endl;
cout << d[1][0] << " " << d[1][1] << " " << d[1][2] << endl;
// 2.2 2.2 2.2
// 2.2 2.2 2.2
这几个比较好理解,下面不好理解:
mat2x3 e( vec3(2, 2, 2), vec3(3, 4, 5));
vec2 f(2, 3);
vec3 g = e * f;
cout << g[0] << " " << g[1] << " " << g[2] << endl;]
//13 16 19
mat2x3 与 vec2 相乘定义为
可以这样理解:看作 mat2x3的转置乘上向量后结果再转置。
下面同理:
mat2x3 a(vec3(2, 3, 4), vec3(2, 2, 2));
vec3 f(2, 3, 4);
vec2 g = f*a;
cout << g[0] << " " << g[1] << endl;
//29 18
vec3 与 mat2x3 的相乘定义为:
可以这样理解:看作 mat2x3的转置乘上向量后结果再转置。
值得注意的是, mat2x3 与 mat2x2 相乘定义为 mat2x2 与 mat2x3 相乘,实际上相乘的顺序做了调整 。
值得注意的是, mat2x3 与 mat3x2 相乘定义为 mat3x2 与 mat2x3 相乘,实际上相乘的顺序做了调整 。
值得注意的是, mat2x3 与 mat3x2 相乘定义为 mat3x2 与 mat2x4 相乘,实际上相乘的顺序做了调整, 这里不再列出公式,大家可以去自行验证 。
mat2x3 a(vec3(2, 3, 4), vec3(2, 2, 2));
mat2x2 b(vec3(2, 3), vec3(2, 5));
mat2x3 c=a*b;
mat2x3 d(vec3(2, 3, 4), vec3(2, 2, 9));
mat3x3 e=a*d;
mat2x4 f(vec3(2, 3, 4, 1.1), vec3(2, 2, 9 ,0.1));
mat4x3 g=a*f;
剩下就是两个除法,要么是矩阵除以标量,则每个分量除以标量 ; 要么是分量除以矩阵,矩阵的每个分量取倒数。
mat2x3 a(vec3(2, 3, 4), vec3(2, 2, 2));
float b=2;
mat2x3 c=a / b;
cout<< c[0][0] << " " << c[0][1] << " " << c[0][2] <<endl;
cout<< c[1][0] << " " << c[1][1] << " " << c[1][2];
//1 1.5 2
//1 1 1
mat2x3 d=b/a;
cout<< d[0][0] << " " << d[0][1] << " " << d[0][2] <<endl;
cout<< d[1][0] << " " << d[1][1] << " " << d[1][2];
//1 0.666667 0.5
//1 1 1
列优先存储的问题
因为 glm 和 opengl 中所有矩阵都是列优先存储的,所以我们对于两个列优先矩阵相乘或者一个列优先矩阵和一个向量乘法该如何思考呢?
我们不妨可以把 glm 意义下的矩阵转置变成行优先存储的矩阵,再来进行我们想要的变换或相乘,进行后再将其转置放入 glm 中参与后面的运算。