一、实现内容:
本篇文章将介绍采用Visual Studio2019 + Qt +OpenGL在Qt窗口中利用OpenGL Widget组件实现通过鼠标控制物体绕坐标轴旋转、平移和缩放等功能。
二、前期基础:
理解本篇内容需要一定的Qt和OpenGL基础,本文将采用一个带有纹理的立方体作为演示主体,因为本文的核心内容为采用Qt和OpenGL实现鼠标控制物体绕坐标轴旋转、平移和缩放等功能,所以有关如何采用OpenGL绘制物体、添加纹理将不再介绍,请参考我本系列博客之前言部分进行相关内容的学习:Windows系统下采用Visual Studio + QT + OpenGL实现RVIZ显示平面网格、坐标系和三维激光点云等功能(前言)
三、实现原理:
3.1、OpenGL中的坐标系统(此处引用的LearnOpenGL教程的内容)
为了将坐标从一个坐标系变换到另一个坐标系,我们需要用到几个变换矩阵,最重要的几个分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。我们的顶点坐标起始于局部空间(Local Space),在这里它称为局部坐标(Local Coordinate),它在之后会变为世界坐标(World Coordinate),观察坐标(View Coordinate),裁剪坐标(Clip Coordinate),并最后以屏幕坐标(Screen Coordinate)的形式结束。下面的这张图展示了整个流程以及各个变换过程做了什么:
(1)局部坐标是对象相对于局部原点的坐标,也是物体起始的坐标。
(2)下一步是将局部坐标变换为世界空间坐标,世界空间坐标是处于一个更大的空间范围的。这些坐标相对于世界的全局原点,它们会和其它物体一起相对于世界的原点进行摆放。
(3)接下来我们将世界坐标变换为观察空间坐标,使得每个坐标都是从摄像机或者说观察者的角度进行观察的。
(4)坐标到达观察空间之后,我们需要将其投影到裁剪坐标。裁剪坐标会被处理至-1.0到1.0的范围内,并判断哪些顶点将会出现在屏幕上。
(5)最后,我们将裁剪坐标变换为屏幕坐标,我们将使用一个叫做视口变换(Viewport Transform)的过程。视口变换将位于-1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。
详细介绍可参考LearnOpenGL教程中的坐标系统。
3.2、OpenGL中物体绕坐标轴旋转
了解OpenGL坐标系统系统之后,我们应该明白要想使物体绕坐标轴旋转应该改变其模型矩阵,通过实时改变模型矩阵中的旋转角度,进而实现物体的旋转。OpenGL中的坐标系是右手坐标系,其X轴朝向右方、Y轴朝向正上方、Z轴朝向我们。而我们实际用到的坐标系更可能是Z轴朝向正上方,因此通过鼠标左键控制物体在三维空间中绕坐标轴旋转,应该绕X轴和Z轴旋转。实现部分代码如下:
旋转部分核心代码:
QMatrix4x4 projection, view, model;
model.rotate(x_rotate, 1.0, 0.0, 0.0); //绕X轴旋转
model.rotate(z_rotate, 0.0, 0.0, 1.0); //绕Z轴旋转
void opengl_widget::mouseMoveEvent(QMouseEvent* event)
{
static QPoint lastPos;
auto currentPos = event->pos();
QPoint deltaPos;
if (leftMousePress == true)
{
if (first_mouse == true)
{
lastPos = currentPos;
first_mouse = false;
}
deltaPos = currentPos - lastPos;
lastPos = currentPos;
x_rotate = x_rotate + 0.3 * deltaPos.y();
z_rotate = z_rotate + 0.3 * deltaPos.x();
if (x_rotate > 30.0f)
x_rotate = 30.0f;
if (x_rotate < -120.0f)
x_rotate = -120.0f;
}
update();
}
3.3、OpenGL中物体绕坐标轴平移
鼠标中轴拖动物体的移动,也应该改变其模型矩阵,通过实时改变位移矩阵的值,来实现物体的移动效果。旋转和平移叠加在一起时,应该先旋转后平移,实现部分代码如下:
平移部分核心代码:
model.translate(x_trans, y_trans, 0.0);
model.rotate(x_rotate, 1.0, 0.0, 0.0);
model.rotate(z_rotate, 0.0, 0.0, 1.0);
void opengl_widget::mouseMoveEvent(QMouseEvent* event)
{
static QPoint lastPos;
auto currentPos = event->pos();
QPoint deltaPos;
if (MidMousePress == true)
{
if (first_mouse == true)
{
lastPos = currentPos;
first_mouse = false;
}
deltaPos = currentPos - lastPos;
lastPos = currentPos;
x_trans = x_trans + 0.01 * deltaPos.x();
y_trans = y_trans - 0.01 * deltaPos.y();
}
update();
}
3.4、OpenGL中物体缩放
鼠标中轴滚动实现物体的缩放应该改变坐标系统的投影矩阵,其实现部分代码如下:
缩放部分核心代码:
QMatrix4x4 projection, view, model;
projection.perspective(zoom , (float)width() / (float)height(), 1.0f, 100.0f);
void opengl_widget::wheelEvent(QWheelEvent* event)
{
auto scroll_offest = event->angleDelta().y() / 120;
zoom = zoom - (float)scroll_offest;
if (zoom < 10.0f)
{
zoom = 10.0f;
}
if (zoom > 80.0f)
{
zoom = 80.0f;
}
update();
}
四、完整实现代码:
完整代码的实现建立在上篇博客的基础上,此外,还需再添加两个着色器文件和一个纹理图片,着色器程序代码如下所示,opengl_widget.h和opengl_widget.cpp文件代码较多,此处不再展示,可在Gitee开源代码中查看:Windows系统下采用Visual Studio + QT + OpenGL实现RVIZ显示平面网格、坐标系和三维激光点云等功能(一)
顶点着色器shader.vs:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexture;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoord = aTexture;
}
片段着色器shader.fs:
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1 , TexCoord);
}
五、效果展示:
鼠标控制物体绕坐标轴旋转、平移和缩放效果图
六、总结:
有些内容可能表述的不够清晰,有什么问题可评论留言或私信我。
Gitee开源代码网址:https://gitee.com/wccworld/learn_qt_opengl
下一篇博客链接地址:
下一篇博客将介绍QT中采用OpenGL Widget组件实现平面网格和三维坐标轴的显示。
Windows系统下采用Visual Studio + QT + OpenGL实现RVIZ显示平面网格、坐标系和三维激光点云等功能(三)