GLM 库主要用于代数运算,与 Eigen、Blas 库类似。但由于 GLM 库主要应用场景是坐标变换和摄像机,因此维数受限,功能弱于 Eigen、Blas。glm 的主要优势在于 GLM 提供了使用与 GLSL(OpenGL着色语言)相同的命名约定和功能设计和实现的类和函数,因此任何了解 GLSL 的人都可以在 C++ 中使用 GLM。根据官方文档介绍, GLM 全称是 OpenGL Mathematics,是一个基于 OpenGL 着色语言的用于图形软件的纯 C++的数学库。但是就算脱离 OpenGL 环境,也可以确保能够正常使用。这篇文章首先主要详解 GLM 库的两个数据结构 vec 和 mat。
vec 表示向量 , glm 库中有四个头文件定义了 vec ,分别是 type_vec1.hpp , type_vec2.hpp , type_vec3.hpp 和 type_vec4.hpp。分别表示一到四维的向量。
首先是一维向量的定义。
注意到这段关于 vec1 的构造函数的声明代码:
template <typename T,qualifier Q>
struct vec <1 ,T,Q>{
GLM_FUNC_DECL GLM_CONSTEXPR vec (vec const & v) GLM_DEFAULT ;
template <qualifier P>
GLM_FUNC_DECL GLM_CONSTEXPR vec (vec<1 , T, P> const & v) ;
GLM_FUNC_DECL GLM_CONSTEXPR explicit vec (T scalar) ;
template <typename U, qualifier P>
GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec (vec<1 , U, P> const & v) ;
这里发现模板里面有个 qualifier Q,查看 qualifier.hpp 文件可以查看其定义:
enum qualifier
packed_highp,
packed_mediump,
packed_lowp,
# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE
aligned_highp,
aligned_mediump,
aligned_lowp,
aligned = aligned_highp,
# endif
highp = packed_highp,
mediump = packed_mediump,
lowp = packed_lowp,
packed = packed_highp,
# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE && defined(GLM_FORCE_DEFAULT_ALIGNED_GENTYPES)
defaultp = aligned_highp
# else
defaultp = highp
# endif
根据其注释发现是与精度有关,一般使用defaultp就行。
查看构造函数的定义:
template <typename T, qualifier Q>
template <qualifier P>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1 , T, Q>::vec (vec<1 , T, P> const & v)
: x (v.x)
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1 , T, Q>::vec (T scalar)
: x (scalar)
template <typename T, qualifier Q>
template <typename U, qualifier P>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1 , T, Q>::vec (vec<1 , U, P> const & v)
: x (static_cast <T>(v.x))
那么根据构造函数的声明和定义,一般我们可以按照两种方式初始化,要么直接传入一个标量,要么直接传入入另外一个 vec,但是数据类型也就是 typename 要相同:
vec<1,int ,defaultp> a1 (1 ) ;
vec<1,int ,defaultp> a2 (a1) ;
也可以依靠static_cast来进行类型转换:
vec<1,int ,defaultp> a1 (1 ) ;
vec<1,float ,defaultp> a2 (a1) ;
接着看 vec 这个类有哪些属性和方法:
struct vec <1 ,T,Q>{
union {T x, r, s;};
GLM_FUNC_DECL static GLM_CONSTEXPR length_type length () {return 1 ;}
GLM_FUNC_DECL GLM_CONSTEXPR T & operator [](length_type i);
GLM_FUNC_DECL GLM_CONSTEXPR T const & operator [](length_type i) const ;
还有很多运算符的重载,大家可以去看 type_vec1.hpp 和 type_vec1.inl。这里主要说关于属性的访问,可以看到有一个匿名 union ,可以通过 x,r,s 三个属性去访问。 []的重载定义如下,无论输入任何数字都是返回 x ,这个定义很符合一维向量的特性,比较省事:
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<1 , T, Q>::operator [](typename vec<1 , T, Q>::length_type)
return x;
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const & vec<1 , T, Q>::operator [](typename vec<1 , T, Q>::length_type) const
return x;
可以通过以下形式访问属性:
vec<1,int ,defaultp> vec1 (2 ) ;
cout<<vec1.x<<endl;
cout<<vec1.r<<endl;
cout<<vec1.s<<endl;
cout<<vec1[0 ]<<endl;
cout<<vec1[10 ]<<endl;
cout<<vec1.length ();
现在铺垫说完了,因为我们一般用的头文件是 glm.hpp ,而不是 vec1.hpp,glm.hpp 用头文件 fwd.hpp 包装了 vec1.hpp。
在 fwd.hpp 中做了如下的声明:
typedef vec<1 , float , defaultp> vec1;
typedef vec<1 , int , defaultp> ivec1;
typedef vec<1 , uint, defaultp> uvec1;
因此我们一般按照如下使用:
ivec1 c (1 ) ;
vec1 d (1.55 ) ;
vec1 a (1.2 ) ;
ivec1 b (a) ;
cout << b[1 ] << endl;
底层调用构造函数 vec<1, T, Q>::vec(vec<1, U, P> const& v) 来进行强制类型转换。
再来看四维向量,也就是 type_vec4.hpp 头文件。
分别看构造函数的声明代码( type_vec4.hpp ):
template <typename T, qualifier Q>
struct vec <4 , T, Q>
GLM_FUNC_DECL GLM_CONSTEXPR vec (vec<4 , T, Q> const & v) GLM_DEFAULT ;
template <qualifier P>
GLM_FUNC_DECL GLM_CONSTEXPR vec (vec<4 , T, P> const & v) ;
GLM_FUNC_DECL GLM_CONSTEXPR explicit vec (T scalar) ;
GLM_FUNC_DECL GLM_CONSTEXPR vec (T x, T y, T z, T w) ;
template <typename U, qualifier P>
GLM_FUNC_DECL GLM_CONSTEXPR explicit vec (vec<1 , U, P> const & v) ;
再来看看 vec4 构造函数的定义:
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4 , T, Q>::vec (vec<4 , T, Q> const & v)
: x (v.x), y (v.y), z (v.z), w (v.w)
template <typename T, qualifier Q>
template <qualifier P>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4 , T, Q>::vec (vec<4 , T, P> const & v)
: x (v.x), y (v.y), z (v.z), w (v.w)
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4 , T, Q>::vec (T scalar)
: x (scalar), y (scalar), z (scalar), w (scalar)
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4 , T, Q>::vec (T _x, T _y, T _z, T _w)
: x (_x), y (_y), z (_z), w (_w)
template <typename T, qualifier Q>
template <typename U, qualifier P>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4 , T, Q>::vec (vec<1 , U, P> const & v)
: x (static_cast <T>(v.x))
, y (static_cast <T>(v.x))
, z (static_cast <T>(v.x))
, w (static_cast <T>(v.x))
根据以上 vec4 的声明和定义可以按照以下方式构造:
vec<4,int ,defaultp> a (10 ) ;
vec<4,int ,defaultp> b (10 ,20 ,30 ,40 ) ;
vec<1,int ,defaultp> c (1 ) ;
vec<4,int ,defaultp> d (c) ;
vec<4,int ,defaultp> e (d) ;
同理 fwd.hpp 也对 vec4 做了相应的包装,只要使用头文件 <glm/glm.hpp> 就可以了。
再来看看 vec4 的属性和方法。
union { T x, r, s; };
union { T y, g, t; };
union { T z, b, p; };
union { T w, a, q; };
GLM_FUNC_DECL static GLM_CONSTEXPR length_type length () {return 4 ;}
GLM_FUNC_DECL GLM_CONSTEXPR T & operator [](length_type i);
GLM_FUNC_DECL GLM_CONSTEXPR T const & operator [](length_type i) const ;
再看 vec4.inl 文件:
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR T& vec<4 , T, Q>::operator [](typename vec<4 , T, Q>::length_type i)
assert (i >= 0 && i < this ->length ());
switch (i)
default :
case 0 :
return x;
case 1 :
return y;
case 2 :
return z;
case 3 :
return w;
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const & vec<4 , T, Q>::operator [](typename vec<4 , T, Q>::length_type i) const
assert (i >= 0 && i < this ->length ());
switch (i)
default :
case 0 :
return x;
case 1 :
return y;
case 2 :
return z;
case 3 :
return w;
其他更多运算符的重载可以查看头文件。
那么我们可以访问通过 [] 或者 属性访问符访问 xyzw 、rgba 、 stpq。
mat 表示矩阵。关于 mat ,glm 支持行列分别为1到4维。为了一定的广义性, 我们这里分析 type_mat4x3.hpp ,其他的头文件也是以 type_matrxk.hpp(r,k都为1,2,3或者4)。
首先定义了 col_type 、 row_type 、type 、transpose_type 和 value_type 五种数据类型。
template <typename T, qualifier Q>
struct mat <4 , 3 , T, Q>
typedef vec<3 , T, Q> col_type;
typedef vec<4 , T, Q> row_type;
typedef mat<4 , 3 , T, Q> type;
typedef mat<3 , 4 , T, Q> transpose_type;
typedef T value_type;
定义 col_type 表示行向量的数据类型, row_type 表示列向量的数据类型, type 表示这个矩阵的数据类型。
除了数据类型,mat 还定义了一些属性:
col_type value[4 ];
typedef length_t length_type;
GLM_FUNC_DECL static GLM_CONSTEXPR length_type length () { return 4 ; }
GLM_FUNC_DECL col_type & operator [](length_type i);
GLM_FUNC_DECL GLM_CONSTEXPR col_type const & operator [](length_type i) const ;
重载[]运算符来进行访问:
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER typename mat<4 , 3 , T, Q>::col_type & mat<4 , 3 , T, Q>::operator [](typename mat<4 , 3 , T, Q>::length_type i)
assert (i < this ->length ());
return this ->value[i];
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4 , 3 , T, Q>::col_type const & mat<4 , 3 , T, Q>::operator [](typename mat<4 , 3 , T, Q>::length_type i) const
assert (i < this ->length ());
return this ->value[i];
通过源码发现[]可以访问一行数据,也就是返回 col_type 类型的数据,因此[]可以代入0,1,2,3分别为第一到四行。再根据vec的[]访问每个分量。
再来看看构造函数的声明:
<typename T, qualifier Q>
struct mat <4 , 3 , T, Q>{
GLM_FUNC_DECL GLM_CONSTEXPR mat () GLM_DEFAULT ;
template <qualifier P>
GLM_FUNC_DECL GLM_CONSTEXPR mat (mat<4 , 3 , T, P> const & m) ;
GLM_FUNC_DECL explicit GLM_CONSTEXPR mat (T const & x) ;
GLM_FUNC_DECL GLM_CONSTEXPR mat (
T const & x0, T const & y0, T const & z0,
T const & x1, T const & y1, T const & z1,
T const & x2, T const & y2, T const & z2,
T const & x3, T const & y3, T const & z3) ;
GLM_FUNC_DECL GLM_CONSTEXPR mat (
col_type const & v0,
col_type const & v1,
col_type const & v2,
col_type const & v3) ;
接下来看构造函数的定义:
template <typename T, qualifier Q>
template <qualifier P>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4 , 3 , T, Q>::mat (mat<4 , 3 , T, P> const & m)
#if GLM_HAS_INITIALIZER_LISTS
:value{col_type (m[0 ]), col_type (m[1 ]), col_type (m[2 ]), col_type (m[3 ])}
#endif
#if !GLM_HAS_INITIALIZER_LISTS
this ->value[0 ] = m[0 ];
this ->value[1 ] = m[1 ];
this ->value[2 ] = m[2 ];
this ->value[3 ] = m[3 ];
#endif
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4 , 3 , T, Q>::mat (T const & s)
#if GLM_HAS_INITIALIZER_LISTS
: value{col_type (s, 0 , 0 ), col_type (0 , s, 0 ), col_type (0 , 0 , s), col_type (0 , 0 , 0 )}
#endif
#if !GLM_HAS_INITIALIZER_LISTS
this ->value[0 ] = col_type (s, 0 , 0 );
this ->value[1 ] = col_type (0 , s, 0 );
this ->value[2 ] = col_type (0 , 0 , s);
this ->value[3 ] = col_type (0 , 0 , 0 );
#endif
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4 , 3 , T, Q>::mat
T const & x0, T const & y0, T const & z0,
T const & x1, T const & y1, T const & z1,
T const & x2, T const & y2, T const & z2,
T const & x3, T const & y3, T const & z3
#if GLM_HAS_INITIALIZER_LISTS
: value{col_type (x0, y0, z0), col_type (x1, y1, z1), col_type (x2, y2, z2), col_type (x3, y3, z3)}
#endif
#if !GLM_HAS_INITIALIZER_LISTS
this ->value[0 ] = col_type (x0, y0, z0);
this ->value[1 ] = col_type (x1, y1, z1);
this ->value[2 ] = col_type (x2, y2, z2);
this ->value[3 ] = col_type (x3, y3, z3);
#endif
template <typename T, qualifier Q>
GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4 , 3 , T, Q>::mat (col_type const & v0, col_type const & v1, col_type const & v2, col_type const & v3)
#if GLM_HAS_INITIALIZER_LISTS
: value{col_type (v0), col_type (v1), col_type (v2), col_type (v3)}
#endif
#if !GLM_HAS_INITIALIZER_LISTS
this ->value[0 ] = v0;
this ->value[1 ] = v1;
this ->value[2 ] = v2;
this ->value[3 ] = v3;
#endif
再由于 <glm/glm.hpp> 对type_mat4x3.hpp 的包装,因此有以下的构造 mat:
vec3 a (1 , 2 , 3 ) ;
vec3 b (4 , 5 , 6 ) ;
vec3 c (7 , 8 , 9 ) ;
vec3 d (10 , 11 , 12 ) ;
mat4x3 mat1 (a, b, c, d) ;
mat4x3 mat2 (1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ) ;
mat4x3 mat3 (2 ) ;
mat4x3 mat4 (mat3) ;
如果要观察这个矩阵,可以用两层循环,外层是从0循环到length(),内层是col_type的length()。以下以构造函数只传入一个基本数据类型为例来演示打印:
mat4x3 mat (2 ) ;
for (int i = 0 ; i < mat.length (); ++i) {
for (int j = 0 ; j < mat[0 ].length (); ++j) {
cout << mat[i][j] << " " ;
cout << endl;
其他的运算符重载涉及一些矩阵运算,打算在后面文章中详细讨论,有些定义有点反人类。
503
半岛铁盒里的猫
OpenGL
音视频开发
Android
254
半岛铁盒里的猫
OpenGL
计算机图形学
音视频开发
237
Timmy_zzh
OpenGL
Android
387
古柳_Deserts_X
three.js
WebGL
OpenGL