Qt+OpenGL实现变换(缩放、位移和旋转)

尽管现在已经知道了如何创建一个物体、着色、加入纹理,给它们一些细节的表现,但因为它们都还是静态的物体,仍是不够有趣。可以尝试着在每一帧改变物体的顶点并且重配置缓冲区从而使它们移动,但这太繁琐了,而且会消耗很多的处理时间。现在有一个更好的解决方案,使用(多个)矩阵(Matrix)对象可以更好的变换(Transform)一个物体。

下面是一个2×3矩阵的例子:

                                               

矩阵可以通过(i, j)进行索引,i是行,j是列,这就是上面的矩阵叫做2×3矩阵的原因(3列2行,也叫做矩阵的维度(Dimension))。这与在索引2D图像时的(x, y)相反,获取4的索引是(2, 1)(第二行,第一列)(译注:如果是图像索引应该是(1, 2),先算列,再算行)。

缩放

对一个向量进行缩放(Scaling)就是对向量的长度进行缩放,而保持它的方向不变。由于进行的是2维或3维操作,可以分别定义一个有2或3个缩放变量的向量,每个变量缩放一个轴(x、y或z)。

先来尝试缩放向量v¯=(3,2)𝑣¯=(3,2)。可以把向量沿着x轴缩放0.5,使它的宽度缩小为原来的二分之一;将沿着y轴把向量的高度缩放为原来的两倍。看看把向量缩放(0.5, 2)倍所获得的s¯是什么样的:

OpenGL通常是在3D空间进行操作的,对于2D的情况可以把z轴缩放1倍,这样z轴的值就不变了。刚刚的缩放操作是不均匀(Non-uniform)缩放,因为每个轴的缩放因子(Scaling Factor)都不一样。如果每个轴的缩放因子都一样那么就叫均匀缩放(Uniform Scale)。

下面会构造一个变换矩阵来为我们提供缩放功能。从单位矩阵了解到,每个对角线元素会分别与向量的对应元素相乘。如果把1变为3会怎样?这样子的话,就把向量的每个元素乘以3了,这事实上就把向量缩放3倍。如果把缩放变量表示为(S1,S2,S3)我们可以为任意向量(x,y,z)(x,y,z)定义一个缩放矩阵:

注意,第四个缩放向量仍然是1,因为在3D空间中缩放w分量是无意义的。w分量另有其他用途,在后面会看到。

位移

位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程,从而在位移向量基础上移动了原始向量。已经讨论了向量加法,所以这应该不会太陌生。

和缩放矩阵一样,在4×4矩阵上有几个特别的位置用来执行特定的操作,对于位移来说它们是第四列最上面的3个值。如果把位移向量表示为(Tx,Ty,Tz),就能把位移矩阵定义为:

旋转

上面几个的变换内容相对容易理解,在2D或3D空间中也容易表示出来,但旋转(Rotation)稍复杂些。

首先来定义一个向量的旋转到底是什么。2D或3D空间中的旋转用角(Angle)来表示。角可以是角度制或弧度制的,周角是360角度或2 PI弧度。

大多数旋转函数需要用弧度制的角,但幸运的是角度制的角也可以很容易地转化为弧度制的:

  • 弧度转角度:角度 = 弧度 * (180.0f / PI)
  • 角度转弧度:弧度 = 角度 * (PI / 180.0f)

PI约等于3.14159265359。

转半圈会旋转360/2 = 180度,向右旋转1/5圈表示向右旋转360/5 = 72度。下图中展示的2D向量v¯𝑣¯是由k¯𝑘¯向右旋转72度所得的:

在3D空间中旋转需要定义一个角一个旋转轴(Rotation Axis)。物体会沿着给定的旋转轴旋转特定角度。如果想要更形象化的感受,可以试试向下看着一个特定的旋转轴,同时将头部旋转一定角度。当2D向量在3D空间中旋转时,把旋转轴设为z轴(尝试想象这种情况)。

使用三角学,给定一个角度,可以把一个向量变换为一个经过旋转的新向量。这通常是使用一系列正弦和余弦函数(一般简称sin和cos)各种巧妙的组合得到的。

旋转矩阵在3D空间中每个单位轴都有不同定义,旋转角度用θ𝜃表示:

沿x轴旋转:

沿y轴旋转:

沿z轴旋转:

矩阵的组合

使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,可以把多个变换组合到一个矩阵中。看看是否能生成一个变换矩阵,让它组合多个变换。假设有一个顶点(x, y, z),希望将其缩放2倍,然后位移(1, 2, 3)个单位。需要一个位移和缩放矩阵来完成这些变换。结果的变换矩阵看起来像这样:

注意,当矩阵相乘时先写位移再写缩放变换的。矩阵乘法是不遵守交换律的,这意味着它们的顺序很重要。当矩阵相乘时,在最右边的矩阵是第一个与向量相乘的,所以应该从右向左读这个乘法。建议在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移,否则它们会(消极地)互相影响。比如,如果先位移再缩放,位移的向量也会同样被缩放(译注:比如向某方向移动2米,2米也许会被缩放成1米)!

用最终的变换矩阵左乘的向量会得到以下结果:

                 

用Qt实现

创建一个QMatrix4x4  对象matrix ,用于存储变换矩阵。

获取当前时间的毫秒数,用于计算旋转角度。QTime::currentTime().msec()返回当前时间的毫秒部分,范围是 0 到 999。

将矩阵matrix 旋转time度。matrix.rotate 方法接受一个角度和一个旋转轴的向量。在这个例子中,旋转轴是 (0.0f, 0.0f, 1.0f),表示绕 Z 轴旋转.

将矩阵 matrix 作为统一变量传递给着色器程序.setUniformValue方法用于将值设置为指定的着色器统一变量。在这个例子中,theMatrix是着色器中的统一变量名称。

QMatrix4x4 matrix;
    unsigned int time=QTime::currentTime().msec();
    matrix.rotate(time,0.0f,0.0f,1.0f);
    shaderProgram.setUniformValue("theMatrix",matrix);

在着色器中,theMatrix变量应该被定义为一个 4x4 的矩阵,并应用到顶点坐标的变换中

#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 theMatrix;
void main() {
    gl_Position = theMatrix * vec4(aPos, 1.0);
}

实现了通过时间的变化生成旋转角度,使用 QMatrix4x4生成旋转矩阵,并将该矩阵传递给着色器以实现动画效果。运行结果如下图:

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值