本篇完整工程见gitee:QTOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主。
我们需要改变物体的位置
现有解决办法(每一帧,改变顶点位置(所有顶点))
每个顶点使用向量表示,使用矩阵表示对应顶点的操作。
向量是有方向和大小的量
a ⃗ = ( x y z ) \vec{a} = \left(\begin{matrix} x \\ y \\z \end{matrix}\right) a= xyz
向量与标量运算
标量(Scalar)只是一个数字(或者说是仅有一个分量的向量)。当把一个向量加/减/乘/除一个标量,我们可以简单的把向量的每个分量分别进行该运算。对于加法来说会像这样:
( 1 2 3 ) + x = ( 1 + x 2 + x 3 + x ) \left(\begin{matrix}1 \\ 2 \\3 \end{matrix}\right) + x = \left(\begin{matrix}1+x \\ 2+x \\3+x \end{matrix}\right) 123 +x= 1+x2+x3+x
其中的+可以是+,-,·或÷,其中·是乘号。注意-和÷运算时不能颠倒(标量-/÷向量),因为颠倒的运算是没有定义的。
向量取反
− a ⃗ = − ( x y z ) = ( − x − y − z ) -\vec{a} = -\left(\begin{matrix}x \\ y \\z \end{matrix}\right)= \left(\begin{matrix}-x \\ -y \\-z \end{matrix}\right) −a=− xyz = −x−y−z
向量加减
a ⃗ = ( 1 2 3 ) , b ⃗ = ( 1 2 3 ) , a ⃗ + b ⃗ = ( 1 + 1 2 + 2 3 + 3 ) = ( 2 4 6 ) \vec{a} = \left(\begin{matrix}1 \\ 2 \\3 \end{matrix}\right),\vec{b} = \left(\begin{matrix}1 \\ 2 \\3 \end{matrix}\right),\vec{a}+\vec{b} = \left(\begin{matrix}1 + 1\\ 2 + 2 \\3 +3 \end{matrix}\right) = \left(\begin{matrix}2 \\ 4 \\ 6 \end{matrix}\right) a= 123 ,b= 123 ,a+b= 1+12+23+3 = 246
长度
∣ v ⃗ ∣ = x 2 + y 2 (1) |\vec{v}|=\sqrt{x^2+y^2} \tag{1} ∣v∣=x2+y2(1)
向量相乘
a ⃗ ⋅ b ⃗ = ∣ a ⃗ ∣ ⋅ ∣ b ⃗ ∣ ⋅ c o s θ \vec{a} \cdot \vec{b} =|\vec{a}|\cdot|\vec{b}|\cdot cos\theta a⋅b=∣a∣⋅∣b∣⋅cosθ
一个矩形的数字、符号或者表达式的数组。矩阵中的每一项叫做矩阵的元素
[ 0 1 1 1 1 0 1 0 1 ] \left[\begin{matrix} 0&1&1\\ 1&1&0\\ 1&0&1\\ \end{matrix}\right] 011110101
矩阵的加减
矩阵与标量之间的加减定义如下
[ 0 1 1 1 ] + ‾ 1 = [ 0 + ‾ 1 1 + ‾ 1 1 + ‾ 1 1 + ‾ 1 ] \left[\begin{matrix} 0&1\\ 1&1\\ \end{matrix}\right] \underline+ 1 = \left[\begin{matrix} 0 \underline +1&1\underline + 1\\ 1\underline + 1&1 \underline + 1\\ \end{matrix}\right] [0111]+1=[0+11+11+11+1]
矩阵与矩阵之间的加减就是两个矩阵对应元素的加减运算
[ 0 1 2 3 ] + ‾ [ 0 1 2 3 ] = [ 0 + ‾ 0 1 + ‾ 1 2 + ‾ 2 3 + ‾ 3 ] 加法 = [ 0 2 4 6 ] 减法 = [ 0 0 0 0 ] \left[\begin{matrix} 0&1\\ 2&3\\ \end{matrix}\right] \underline+ \left[\begin{matrix} 0&1\\ 2&3\\ \end{matrix}\right] = \left[\begin{matrix} 0\underline+0&1\underline+1\\ 2\underline+2&3\underline+3\\ \end{matrix}\right] 加法= \left[\begin{matrix} 0&2\\ 4&6\\ \end{matrix}\right] 减法= \left[\begin{matrix} 0&0\\ 0&0\\ \end{matrix}\right] [0213]+[0213]=[0+02+21+13+3]加法=[0426]减法=[0000]
矩阵的数乘
和矩阵与标量的加减一样,矩阵与标量之间的乘法也是矩阵的每一个元素分别乘以该标量。
[ 0 1 1 1 ] ⋅ 2 = [ 0 ⋅ 2 1 ⋅ 2 1 ⋅ 2 1 ⋅ 2 ] = [ 0 2 2 2 ] \left[\begin{matrix} 0&1\\ 1&1\\ \end{matrix}\right] \cdot 2 = \left[\begin{matrix} 0 \cdot2&1\cdot 2\\ 1\cdot 2&1 \cdot 2\\ \end{matrix}\right]= \left[\begin{matrix} 0&2\\ 2&2\\ \end{matrix}\right] [0111]⋅2=[0⋅21⋅21⋅21⋅2]=[0222]
矩阵相乘
矩阵之间的乘法不见得有多复杂,但的确很难让人适应。矩阵乘法基本上意味着遵照规定好的法则进行相乘。当然,相乘还有一些限制:
向量就是一个 N × 1 N \times 1 N×1的矩阵
单位矩阵就是一个除了对角线以为都是0的 N × N N \times N N×N的矩阵
[ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ] ⋅ [ 1 2 3 4 ] = [ 1 ⋅ 1 2 ⋅ 2 3 ⋅ 3 4 ⋅ 4 ] = [ 1 2 3 4 ] \left[\begin{matrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&1&0\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left[\begin{matrix} 1\\ 2\\ 3\\ 4\\ \end{matrix}\right] = \left[\begin{matrix} 1 \cdot 1\\ 2 \cdot 2\\ 3 \cdot 3\\ 4 \cdot 4\\ \end{matrix}\right] = \left[\begin{matrix} 1\\ 2\\ 3\\ 4\\ \end{matrix}\right] 1000010000100001 ⋅ 1234 = 1⋅12⋅23⋅34⋅4 = 1234
缩放
OpenGL通常是在3D空间进行操作的,对于2D的情况我们可以把z轴缩放1倍,这样z轴的值就不变了。我们刚刚的缩放操作是不均匀(Non-uniform)缩放,因为每个轴的缩放因子(Scaling Factor)都不一样。如果每个轴的缩放因子都一样那么就叫均匀缩放(Uniform Scale)。
[ S 1 0 0 0 0 S 2 0 0 0 0 S 3 0 0 0 0 1 ] ⋅ ( x y z 1 ) = ( S 1 ⋅ x S 2 ⋅ y S 3 ⋅ z 1 ) \left[\begin{matrix} S1&0&0&0\\ 0&S2&0&0\\ 0&0&S3&0\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} S1 \cdot x\\ S2 \cdot y\\ S3 \cdot z\\ 1\\ \end{matrix}\right) S10000S20000S300001 ⋅ xyz1 = S1⋅xS2⋅yS3⋅z1
位移
位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程,从而在位移向量基础上移动了原始向量。我们已经讨论了向量加法,所以这应该不会太陌生。
[ 1 0 0 T x 0 1 0 T y 0 0 1 T z 0 0 0 1 ] ⋅ ( x y z 1 ) = ( x + T x y + T y z + T z 1 ) \left[\begin{matrix} 1&0&0&Tx\\ 0&1&0&Ty\\ 0&0&1&Tz\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} x + Tx\\ y + Ty\\ z + Tz\\ 1\\ \end{matrix}\right) 100001000010TxTyTz1 ⋅ xyz1 = x+Txy+Tyz+Tz1
齐次坐标(Homogeneous Coordinates)
向量的w分量也叫齐次坐标。想要从齐次向量得到3D向量,我们可以把x、y和z坐标分别除以w坐标。我们通常不会注意这个问题,因为w分量通常是1.0。使用齐次坐标有几点好处:它允许我们在3D向量上进行位移(如果没有w分量我们是不能位移向量的),而且下一章我们会用w值创建3D视觉效果。
如果一个向量的齐次坐标是0,这个坐标就是方向向量(Direction Vector),因为w坐标是0,这个向量就不能位移(译注:这也就是我们说的不能位移一个方向)。
旋转
大多数旋转函数需要用弧度制的角:
沿着x轴旋转:
[ 1 0 0 0 0 c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 ] ⋅ ( x y z 1 ) = ( x c o s θ ⋅ y − s i n θ ⋅ z s i n θ ⋅ y + c o s θ ⋅ z 1 ) \left[\begin{matrix} 1&0&0&0\\ 0&cos\theta&-sin\theta&0\\ 0&sin\theta&cos\theta&0\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} x\\ cos\theta\cdot y - sin\theta\cdot z\\ sin\theta\cdot y + cos\theta\cdot z\\ 1\\ \end{matrix}\right) 10000cosθsinθ00−sinθcosθ00001 ⋅ xyz1 = xcosθ⋅y−sinθ⋅zsinθ⋅y+cosθ⋅z1
沿着y轴旋转:
[ c o s θ 0 s i n θ 0 0 1 0 0 − s i n θ 0 c o s θ 0 0 0 0 1 ] ⋅ ( x y z 1 ) = ( c o s θ ⋅ x + s i n θ ⋅ z y − s i n θ ⋅ x + c o s θ ⋅ z 1 ) \left[\begin{matrix} cos\theta&0&sin\theta&0\\ 0&1&0&0\\ -sin\theta&0&cos\theta&0\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} cos\theta\cdot x+ sin\theta\cdot z\\ y \\ -sin\theta\cdot x + cos\theta\cdot z\\ 1\\ \end{matrix}\right) cosθ0−sinθ00100sinθ0cosθ00001 ⋅ xyz1 = cosθ⋅x+sinθ⋅zy−sinθ⋅x+cosθ⋅z1
沿着z轴旋转:
[ c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 0 0 0 0 1 ] ⋅ ( x y z 1 ) = ( c o s θ ⋅ x − s i n θ ⋅ y s i n θ ⋅ x + c o s θ ⋅ y z 1 ) \left[\begin{matrix} cos\theta&-sin\theta&0&0\\ sin\theta&cos\theta&0&0\\ 0&0&1&0\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} cos\theta\cdot x - sin\theta\cdot y\\ sin\theta\cdot x + cos\theta\cdot y \\ z \\ 1\\ \end{matrix}\right) cosθsinθ00−sinθcosθ0000100001 ⋅ xyz1 = cosθ⋅x−sinθ⋅ysinθ⋅x+cosθ⋅yz1
矩阵的组合
使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,我们可以把多个变换组合到一个矩阵中。让我们看看我们是否能生成一个变换矩阵,让它组合多个变换。假设我们有一个顶点(x, y, z),我们希望将其缩放2倍,然后位移(1, 2, 3)个单位。我们需要一个位移和缩放矩阵来完成这些变换。结果的变换矩阵看起来像这样:
[ 1 0 0 1 0 1 0 2 0 0 1 3 0 0 0 1 ] ⋅ [ 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 1 ] = [ 2 0 0 1 0 2 0 2 0 0 2 3 0 0 0 1 ] \left[\begin{matrix} 1&0&0&1\\ 0&1&0&2\\ 0&0&1&3\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left[\begin{matrix} 2&0&0&0\\ 0&2&0&0\\ 0&0&2&0\\ 0&0&0&1\\ \end{matrix}\right] = \left[\begin{matrix} 2&0&0&1\\ 0&2&0&2\\ 0&0&2&3\\ 0&0&0&1\\ \end{matrix}\right] 1000010000101231 ⋅ 2000020000200001 = 2000020000201231
用最终的变换矩阵左乘我们的向量会得到以下结果:
[ 2 0 0 1 0 2 0 2 0 0 2 3 0 0 0 1 ] ⋅ ( x y z 1 ) = ( 2 x + 1 2 y + 2 2 z + 3 1 ) \left[\begin{matrix} 2&0&0&1\\ 0&2&0&2\\ 0&0&2&3\\ 0&0&0&1\\ \end{matrix}\right] \cdot \left(\begin{matrix} x\\ y\\ z\\ 1\\ \end{matrix}\right) = \left(\begin{matrix} 2x + 1\\ 2y + 2\\ 2z + 3 \\ 1\\ \end{matrix}\right) 2000020000201231 ⋅ xyz1 = 2x+12y+22z+31
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 1) in vec2 aTexCord;
out vec3 ourColor; // 向片段着色器输出一个颜色
out vec2 texCord; // 向片段着色器输出一个颜色
uniform mat4 RotationMatrix;
void main()
{
gl_Position = RotationMatrix * vec4(aPos, 1.0);
ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
texCord = aTexCord;
}
QMatrix4x4 matrix;
unsigned int time = QTime::currentTime().msec();
matrix.translate(3,3);
matrix.rotate(time, 0.0f, 0.0f, 1.0f);
...
shader_program_.setUniformValue("RotationMatrix", matrix);