本文根据教程:ogldev进行扩充学习,一步步从零开始,记录学习历程
一、准备工作
1.1 表示一个矩阵
我们需要在程序中使用矩阵,我们需要用计算机程序语言来表示出来一个矩阵。
我们用数组来表示一个矩阵,并且规定列填充优先,即一个4*4矩阵表示为:
// 4 * 4 矩阵:
// 0 4 8 12
// 1 5 9 13
// 2 6 10 14
// 3 7 11 15
typedef float Matrix44f[16];
此时如果要将一个4*4的矩阵设置为单位矩阵可以这样操作:
inline void LoadIdentity44(Matrix44f m)
{
m[0] = 1.0f; m[4] = 0.0f; m[8] = 0.0f; m[12] = 0.0f;
m[1] = 0.0f; m[5] = 1.0f; m[9] = 0.0f; m[13] = 0.0f;
m[2] = 0.0f; m[6] = 0.0f; m[10] = 1.0f; m[14] = 0.0f;
m[3] = 0.0f; m[7] = 0.0f; m[11] = 0.0f; m[15] = 1.0f;
}
1.2 矩阵相乘
学过线性代数的同学应该都应该记得矩阵的乘法,这里为了更加扎实的进一步学习,先复习一下矩阵的乘法。
百度百科中的讲解:矩阵乘法
- 首先要明确只有当矩阵A的列数等于矩阵B的行数时,A和B才可以相乘
- 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数
- 乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和
代码种得到两个矩阵相乘的结果:
inline void MatrixMultiply44(Matrix44f product, const Matrix44f a, const Matrix44f b)
{
unsigned int j, k;
for (unsigned int i = 0; i < 16; i++) {
j = i % 4;
k = i / 4 * 4;
product[i] = a[j] * b[k] + a[j + 4] * b[k + 1] + a[j + 8] * b[k + 2] + a[j + 12] * b[k + 3];
}
}
1.3 角度和弧度
弧度的定义:弧长等于半径的弧,其所对的圆心角为1弧度
一周的弧度是2π,一周的角度是360°
弧度= 角度 * ( π / 180 )
角度 = 弧度 * ( 180 / π )
我们在程序中使用的sinf(),cosf(),tanf()参数都是弧度,如果我们使用角度,则需要把角度转化为弧度
代码中角度弧度互相转化
#define PI (3.14159265358979323846)
#define PI_DIV_180 (0.017453292519943296)
#define INV_PI_DIV_180 (57.2957795130823229)
#define DegToRad(x) ((x)*PI_DIV_180)
#define RadToDeg(x) ((x)*INV_PI_DIV_180)
二、平移变换
平移变换示意图如下:
我们的任务便是找到一个矩阵M(称为平移矩阵),对于给定的点P(x,y,z)和平移向量V(v1,v2,v3),使得M*P=P1(x+v1,y+v2,z+v3)
最后通过种种努力,发现了一个4*4的矩阵可以达到这种效果:
- 像这样使用4维向量表示一个三维向量叫做齐次坐标
- 4维向量的四个分量分别是X、Y、Z、W,之前看到的着色器中的内部变量gl_Position就是一个四维向量
- 通常表示点的矩阵让W=1,表示向量的矩阵让W=0,因为点可以被做变换而向量不可以
- 总之先不要管不懂的地方,只要知道平移矩阵我们已经找到了,我们只需要在程序中把平移矩阵与我们的图形的位置坐标相乘就能得到平移后的图形了
在代码中设置一个平移矩阵:
inline void TranslationMatrix44(Matrix44f m, float x, float y, float z)
{
LoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z;
}
三、旋转变换
有了刚才平移变换的基础,我们知道了旋转变换其实也就是找到一个旋转矩阵,来与我们位置坐标相乘能得到旋转后的位置坐标
3.1 推导
旋转变换示意图如下:
示意图为从z轴的正方向朝负方向看,从位置1旋转到位置2(即从(x1,y1)到(x2,y2)),如果圆的半径为1(即是单位圆),则可以得到:
再由高中数学三角函数公式可以推导:
此时找到了旋转α度角之后的x,y坐标,此时只需要找到旋转矩阵与原坐标向量相乘等于新的坐标向量即可
3.2 旋转矩阵
绕z轴旋转(z轴不变):