前三篇链接:

这一周笔者经历了漫长的洲际飞行和昏天黑地的倒时差,所以本篇内容相对少一些,侧重 Qt 而不是 OpenGL。在上一篇中,我们绘制了一个正四面体,然而正四面体的一个特点是无论你从哪个角度看,同时至多只能看到三个面。为了能更好地观察绘制效果,我们尝试变换观察的镜头位置来看被隐藏的面。这一篇中我们将实现动画效果,通过不断旋转被观察的四面体来看到它的四个面。同时我们再通过键盘操控相机位置,从而更好地展示不同相机位置导致的绘制效果变化。

QElapsedTimer 计时

如果要做动画效果,首先我们需要知道程序运行到了什么时间,然后根据时间计算出此时的图像信息,从而进行渲染。不同的操作系统和平台有各种各样的计时 API,但是 Qt 提供了一个简单方便的工具 QElapsedTimer 来完成这件事。其功能非常简单,就是给出从它的 start() 方法执行之后到现在经过了多久的时间。

首先我们先在 PaintingWidget 的声明中添加这个东西,然后在构造函数中初始化它,并启动它。已有的代码我这里直接忽略,请参考前面几篇文章。

class PaintingWidget : public QOpenGLWidget

{

......

private:

......

QElapsedTimer *m_timer;

};

PaintingWidget::PaintingWidget(QWidget* parent){

......

m_timer = new QElapsedTimer;

m_timer->start();

}

接下来在 paintGL() 中,我们只需要调用 m_timer->elapsed() 就可以得到从 start() 到绘图程序执行时刻,经过了多少时间,单位为毫秒。

旋转矩阵

对于一个刚体而言,我们知道其旋转变换是一个线性变换,通过一个旋转矩阵确定。在二维中,图形的旋转是围绕着一个点进行的。众所周知,二维图形围绕原点旋转

角的旋转矩阵是

但是在三维世界里,旋转变得复杂很多,旋转中心不再是一个点,而是一个轴。考虑到图形学中常用的是齐次坐标系,还需要处理第四维的情况。不过 Qt 的 QMatrix4x4 提供了简单的接口帮我们完成这些复杂的数学计算:

void QMatrix4x4::rotate(float angle, const QVector3D &vector);

rotate()函数的第一个参数是旋转的角度,以度数,为单位,而第二个参数是旋转轴,必须以 QVector3D 类型传入。我们在上一篇提到过,MVP矩阵是 P * V * M,物体本身的旋转、放缩等变换就是编码在 M 矩阵中,因此我们在绘图函数中计算 MVP 矩阵的地方,添加旋转代码:

void PaintingWidget::paintGL()

{

QOpenGLFunctions *f = this->context()->functions();

f->glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

f->glClearColor(0.0f, 0.2f, 0.0f, 1.0f);

m_vao->bind();

m_shader->bind();

QMatrix4x4 mvp;

mvp.perspective(45.0f, this->aspectRatio, 0.1f, 100.0f);

mvp.lookAt(QVector3D(0.0f, 3.0f, 0.0f), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0f, 0.0f, 0.0f));

float time_in_second = (float)m_timer->elapsed() / 1000;

mvp.rotate(30.0f * time_in_second, QVector3D(0.7f, 0.5f, 0.2f));

m_shader->setUniformValue(m_shader->uniformLocation("MVP"), mvp);

f->glDrawArrays(GL_TRIANGLES, 0, 4 * 3);

m_shader->release();

m_vao->release();

this->update();

}

这里我们通过 m_timer 获得程序开始运行到此刻的时间,然后以每秒 30° 的速度围绕轴 (0.7, 0.5, 0.2) 进行旋转。这个轴是我随便选的,为的是能够看到四面体的四个面。这段代码的最后一行调用了 update() 方法。在前面我们提到过,这个方法是发出一个绘图请求,但并不立即执行,而是等到下一个处理时间节点再执行。另一个要求重新绘图的方法是 repaint(),这个方法会立即触发绘图,但是 repaint() 会调用 paintGL() 来完成绘图,因此如果在 paintGL() 中调用 repaint() 会导致无穷递归而使程序崩溃。

至此,旋转动画效果就实现了,效果如下图。

键盘控制相机位置

在大多数 OpenGL 教程中,都会介绍如何使用各种工具库(如 GLUT)实现用键盘和鼠标操纵图像。这对于 Qt 来说则更是小菜一碟,毕竟 Qt 的老本行就是做用户界面,有非常完善的各种输入设备的响应机制。

与一般控件的响应机制不同,Qt 中键盘事件的响应机制反倒是很简单,直接通过重载 QWidget 上的各种事件处理器来实现,并不需要使用信号-槽机制。最基本的键盘事件有两种:key press 和 key release,分别由 QWidget::keyPressEvent() 和 QWidget::keyReleaseEvent() 两个事件处理方法进行处理。

这里我仅仅简单实现用方向键控制相机的位置。默认情况下,我们的相机位置在四面体正上方(0, 3, 0)的位置,为了简单起见,我们设置相机观察的中心点为相机位置到 y=0 平面的投影点,用方向键控制前后左右的位置,即相机的 x 坐标和 z 坐标;用正负号键控制相机高度。首先我们需要在 PaintingWidget 的声明部分添加 keyPressEvent() 和相机位置变量 camera_pos:

class PaintingWidget : public QOpenGLWidget

{

......

protected:

......

void keyPressEvent(QKeyEvent *keyEvent);

private:

......

QVector3D camera_pos;

}

在构造函数中,将相机坐标初始化为默认位置,并且还需要使用 setFocusPolicy() 设置这个 Widget 获取焦点的方式,从而确保它能够得到焦点

PaintingWidget::PaintingWidget(QWidget* parent):

QOpenGLWidget (parent), camera_pos(0.0f, 3.0f, 0.0f), ......{

......

setFocusPolicy(Qt::StrongFocus);

}

在绘图的时候,我们需要计算出相机观察的中心点,同样取x正方向为上方向。

QVector3D center(camera_pos);

center.setY(0);

mvp.lookAt(camera_pos, center, QVector3D(1.0f, 0.0f, 0.0f));

这样在重载 keyPressEvent() 的代码中,我们就将右方向键设置为x坐标加一个量,因为 OpenGL 是一个右手系,右方向键就变为z坐标加一个量,最后别忘了请求绘图,代码如下:

void PaintingWidget::keyPressEvent(QKeyEvent *keyEvent){

switch (keyEvent->key()){

case Qt::Key_Right:

camera_pos.setZ(camera_pos.z() + 0.1f);

break;

case Qt::Key_Left:

camera_pos.setZ(camera_pos.z() - 0.1f);

break;

case Qt::Key_Up:

camera_pos.setX(camera_pos.x() + 0.1f);

break;

case Qt::Key_Down:

camera_pos.setX(camera_pos.x() - 0.1f);

break;

case Qt::Key_Plus:

camera_pos.setY(camera_pos.y() + 0.1f);

break;

case Qt::Key_Minus:

camera_pos.setY(camera_pos.y() - 0.1f);

break;

}

update();

}

大功告成,现在这个 demo 开始变得丰富起来,我们可以通过键盘看到相机在不同位置观察物体得到的效果,也更有 3D 的感觉了!

qt 旋转后的三维坐标_OpenGL + Qt: 3 - 旋转动画和键盘操纵相关推荐

  1. 坐标系旋转后的点坐标、坐标点旋转后的点坐标

    坐标系旋转后的点坐标.坐标点旋转后的点坐标 1. 坐标系旋转后的点坐标 2. 坐标点旋转后的点坐标

  2. opengl 如何加阴影_OpenGL + Qt: 3 - 旋转动画和键盘操纵

    前三篇链接: OpenGL + Qt: 0 - 三角形绘制 OpenGL + Qt: 1 - 用下拉框选颜色 OpenGL + Qt: 2 - 走向3D,画正四面体 这一周笔者经历了漫长的洲际飞行和昏 ...

  3. halcon旋转后坐标_基于FPGA的图像旋转设计

    该项目是参加2019届全国大学生FPGA大赛的作品,系统主要实现视频任意角度旋转.利用国产的紫光同创公司的FPGA芯片作为开发平台,视频图像从摄像头实时采集,经过算法旋转后,通过hdmi接口显示.该项 ...

  4. halcon旋转后坐标_FPGA大赛【八】具体模块设计图像旋转方案

    [注]该项目是我们团队参加2019届全国大学生FPGA大赛的作品,系统主要实现视频任意角度旋转.该项目最终晋级决赛,并获得紫光同创企业特别奖.该系列文章介绍我们团队的作品.关注公众号"数字积 ...

  5. 人体动作捕捉格式BVH及其与三维坐标的转换

    BVH简介 BVH是BioVision公司推出的一种人体动作捕捉文件格式.这种文件以节点为核心元素,记录连续数帧内人体骨架的运动. BVH=? 研究一个东西的时候我比较喜欢先研究它的名字.BVH可以认 ...

  6. 坐标的平移,旋转,缩放矩阵

    1.二维坐标的平移,旋转.缩放矩阵 平移矩阵M: 1    0    0 0    1    0 dx  dy   1 D2 = D1*M 旋转矩阵M 设某点与原点连线和X轴夹角为b度,以原点为圆心, ...

  7. CSS练习3D的先旋转后移动和先移动后旋转的区别

    transform:rotate()的旋转是指图片本身(原地转向)的旋转,不是图片整体方位的旋转,旋转以后图片的正方向Z轴会跟着图片的正方向变化. 两个例子(主要区别在注释里) 3D导航栏 !!先移动 ...

  8. 关于图像旋转以及旋转后对应像素的位置

    参考链接:http://www.echojb.com/image/2016/11/14/258268.html 一.首先来说一下关于像素旋转一定角度后的对应位置: (1)旋转中心为左上角原点: 旋转有 ...

  9. Matplotlib画图教程:在QT界面中嵌入三维图片

    Matplotlib画图教程:在QT界面中嵌入三维图片 需求: 做项目报告的时候,有这么一个想法,就是能通过UI随时调用matplotlib进行二维图和三维图的绘制.因此就诞生了做这么一个小模块的想法 ...

最新文章

  1. VC++ 串行化编程实例
  2. C# 通过socket实现UDP 通信
  3. 网站运营过程中经常触碰的几大误区
  4. HBaseConAsia 2019盛会来袭
  5. 解决Git 克隆代码 The remote end hung up unexpectedly错误
  6. swift 如何在IOS应用图标上添加消息数
  7. 【Linux私房菜】第四期——管理
  8. 通俗易懂地解释卷积?
  9. 网站嵌入YouTube视频
  10. 快速找出bug的几点建议
  11. pbe服务器无限火力模式,LOL4月9日PBE更新:无限火力模式上线!
  12. ORACLE 字符集修改,ZHS16GBK转US7ASCII
  13. matlab 固有频率 振型,基于MATLAB语言多自由度振动系统固有频率及主振型计算分析.pdf...
  14. 基于卷积神经网络的句子分类模型【经典卷积分类附源码链接】
  15. 4-3 Coursera吴恩达《卷积神经网络》 第三周课程笔记-目标检测
  16. c 语言 int 转字符串,C++ 字符串string和整数int的互相转化操作
  17. oracle 强制还原一张表,oracle数据库中,用户不小心在生产环境中删除了一张比较重要的表,他想恢复该操作,你会采取什么样的...
  18. 封禁、下架!微信出手了,规范整治数字藏品平台!
  19. 信息论与编码:C语言实现lz78算法
  20. 可视化大屏设计尺寸_Vue 大屏可视化-屏幕自适应(保持设计尺寸比例)

热门文章

  1. 【教程】如何正确的写一个Lemon/Cena的SPJ(special judge)
  2. 《软件需求十步走》阅读笔记一
  3. C#值类型和引用类型的区别
  4. 2018.12.27|区块链技术头条
  5. 2018.12.24-bzoj-2565-最长双回文串
  6. Python中被双下划线包围的魔法方法
  7. go url 参数编码和解码
  8. web.xml的contextConfigLocation作用及自动加载applicationContext.xml
  9. JS打开摄像头并截图上传
  10. Netflix的快速产品集成测试