Qt5+OpenGL 实现彩色正方体显示、缩放、旋转和拖拽平移

您所在的位置:网站首页 qt绘制位图 Qt5+OpenGL 实现彩色正方体显示、缩放、旋转和拖拽平移

Qt5+OpenGL 实现彩色正方体显示、缩放、旋转和拖拽平移

2023-10-08 15:51| 来源: 网络整理| 查看: 265

随手记录Qt学习开发过程中遇到的问题和解决方法

环境:Qt5.12.9 QtCreator4.12.2 MinGW7.3.0

考虑采用OpenGL固定管线绘制图像效率低下,一直想学习采用OpenGL ES的方式进行图像绘制,网上这方面的可参考资料不够丰富, 自己OpenGL的基础又十分欠缺,完全是在一步步自己摸索着学习的。建议跟我一样对OpenGL本身基础就差的同志们,先到https://learnopengl-cn.github.io/这个网站上自学一下,尤其是从“入门→你好,三角形”这部分开始,讲得真的非常清楚,一定要耐心学习。

不废话了,下面就进入正题,把我实现彩色立方体的程序记录一下。

1、首先贴上glsl代码 先是顶点着色器的部分,后是片段着色器的部分。

const char *vshader_code = "uniform mat4 mvp_matrix; \n" "attribute vec4 vPosition; \n" "attribute vec3 aColor; \n" "varying vec3 vColor; \n" "void main() \n" "{ \n" " gl_Position = mvp_matrix * vPosition; \n" " vColor = aColor; \n" "} \n"; const char *fshader_code = "varying vec3 vColor; \n" "void main() \n" "{ \n" " gl_FragColor = vec4(vColor,1.0f); \n" "}

因为这部分代码量太小了,所以没有单独存成glsl文件。这部分内容就不做详细介绍了,不清楚的浏览我上面写的网站进行学习。

2、彩色立方体定义部分代码

#include "cubegeometry.h" CubeGeometry::CubeGeometry():QOpenGLFunctions() , indexBuf(QOpenGLBuffer::IndexBuffer) { initializeOpenGLFunctions(); this->arrayBuf.create(); this->indexBuf.create(); initCubeGeometry(); } CubeGeometry::~CubeGeometry() { this->arrayBuf.destroy(); this->indexBuf.destroy(); } void CubeGeometry::drawCubeGeometry(QOpenGLShaderProgram *program) { this->arrayBuf.bind(); this->indexBuf.bind(); quintptr offset = 0; // Tell OpenGL programmable pipeline how to locate vertex position data int vPos = program->attributeLocation("vPosition"); program->enableAttributeArray(vPos); program->setAttributeBuffer(vPos, GL_FLOAT, offset, 3, sizeof(VertexData)); // Offset for texture coordinate offset += sizeof(QVector3D); // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data int aColor = program->attributeLocation("aColor"); program->enableAttributeArray(aColor); program->setAttributeBuffer(aColor, GL_FLOAT, offset, 3, sizeof(VertexData)); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0); } void CubeGeometry::initCubeGeometry() { //一个正方体有八个顶点,分别对顶点位置和顶点颜色进行定义 VertexData vertices[] = { {QVector3D( 1.0f, 1.0f, 1.0f), QVector3D(1.0f, 0.0f, 0.0f)}, // v0, a-red {QVector3D(-1.0f, 1.0f, 1.0f), QVector3D(1.0f, 0.5f, 0.0f)}, // v1, b-orange {QVector3D(-1.0f, -1.0f, 1.0f), QVector3D(1.0f, 1.0f, 0.0f)}, // v2, c-yellow {QVector3D( 1.0f, -1.0f, 1.0f), QVector3D(0.0f, 1.0f, 0.0f)}, // v3, d-green {QVector3D( 1.0f, 1.0f, -1.0f), QVector3D(0.0f, 1.0f, 1.0f)}, // v4, e-sky {QVector3D(-1.0f, 1.0f, -1.0f), QVector3D(0.0f, 0.0f, 1.0f)}, // v5, f-blue {QVector3D(-1.0f, -1.0f, -1.0f), QVector3D(0.5f, 0.0f, 1.0f)}, // v6, g-purple {QVector3D( 1.0f, -1.0f, -1.0f), QVector3D(1.0f, 1.0f, 1.0f)}, // v7, h-white }; //一个正方形是由两个三角形绘制组成的,因为绘制立方体其实是需要绘制12个三角形,下面定义的就为12个三角形的顶点位置索引 GLushort indices[] = { 0,1,2, 0,2,3, 4,0,3, 4,3,7, 5,4,7, 5,7,6, 1,5,6, 1,6,2, 5,1,0, 5,0,4, 6,7,3, 6,3,2 }; this->arrayBuf.bind(); this->arrayBuf.allocate(vertices, 8 * sizeof(VertexData)); this->arrayBuf.setUsagePattern(QOpenGLBuffer::StaticDraw); this->indexBuf.bind(); this->indexBuf.allocate(indices, 36 * sizeof(GLushort)); this->indexBuf.setUsagePattern(QOpenGLBuffer::StaticDraw); } #ifndef CUBEGEOMETRY_H #define CUBEGEOMETRY_H #include #include #include struct VertexData { QVector3D position; //顶点位置 QVector3D color; //顶点颜色 }; class CubeGeometry : public QOpenGLFunctions { public: CubeGeometry(); ~CubeGeometry(); void drawCubeGeometry(QOpenGLShaderProgram *program);\ private: void initCubeGeometry(); QOpenGLBuffer arrayBuf; //VBO QOpenGLBuffer indexBuf; //IBO }; #endif // CUBEGEOMETRY_H

看一下立方体的显示效果 在这里插入图片描述

3、显示与控制操作 首先创建GLWidget类,继承QOpenGLWidget和QOpenGLFunctions。再创建界面文件,选择添加QWidget对象,并将改对象提升为GLWidget。这样你就会得到一个可以用于显示立方体的界面喽。

插播一个内容,OpenGL常用的有三种透视方式,对应使用不同的透视函数,这里就不展开写了,我代码中使用的是perspective的方式,对应的透视方式如下图所示。 在这里插入图片描述 另外,在使用OpenGL的时候,需要注意对参考坐标系的选择,因为对三维空间中物体的缩放、旋转和平移,都是对空间坐标的运算。通常都会选择被观察物体的中心为中心点建立坐标系。

缩放效果图 在这里插入图片描述 旋转效果图 在这里插入图片描述 拖拽平移效果图在这里插入图片描述

GLWidget类.h文件的代码部分

#ifndef GLWIDGET_H #define GLWIDGET_H #include #include #include #include #include #include #include #include #include "cubegeometry.h" //该结构体用来定义观察者视角的相关信息 typedef struct { float distance; //观察者距被观察物体中心点的初始距离 float fovy; //观察者视角的大小 float zoom; //观察者距被观察物体中心点的距离倍数,用于缩放控制 QVector3D eye; //观察者在被观察物体的三维坐标系中的位置 QVector3D center; //被观察物体的中心点坐标,为[0,0,0] QVector3D up; //观察者的头部朝向 }CameraView; class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: GLWidget(QWidget *parent = nullptr); ~GLWidget(); void Rotate(QMatrix4x4 matrix); protected: void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; void wheelEvent(QWheelEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: void initShaders(); void initTextures(); int setRotation(int angle); void normalizeAngle(int &angle); private: CubeGeometry *cubeGeometry; QOpenGLShaderProgram *program; QMatrix4x4 projection; //透视矩阵 QMatrix4x4 m_translation; //平移矩阵 QMatrix4x4 m_rotation; //旋转矩阵 int m_MouseFlag; //记录按下的鼠标按键 bool m_MousePressFlag; //记录鼠标按键是否已按下 QPoint mousePressPosition; //记录鼠标按下时的坐标位置 CameraView Camera; //记录观察者视角相关数据 int m_xRot; //记录绕x轴旋转的角度 int m_yRot; //记录绕y轴旋转的角度 int m_zRot; //记录绕z轴旋转的角度 qreal m_xTrans; //记录沿x轴移动的位置 qreal m_yTrans; //记录沿y轴移动的位置 }; #endif // GLWIDGET_H

GLWidget类.cpp文件的代码部分

#include "glwidget.h" #include #include #include #include GLWidget::GLWidget(QWidget * parent) : QOpenGLWidget(parent) , cubeGeometry(0) { this->m_MouseFlag = Qt::NoButton; this->m_MousePressFlag = false; this->m_xRot = 0; this->m_yRot = 0; this->m_zRot = 0; this->m_xTrans = 0; this->m_yTrans = 0; } GLWidget::~GLWidget() { makeCurrent(); delete cubeGeometry; doneCurrent(); } void GLWidget::Rotate(QMatrix4x4 matrix) { this->m_rotation = matrix; update(); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); glClearColor(0, 0, 0, 1); initShaders(); initTextures(); // Enable depth buffer glEnable(GL_DEPTH_TEST); // Enable back face culling glEnable(GL_CULL_FACE); this->cubeGeometry = new CubeGeometry(); Camera.distance = 5.0; Camera.fovy = 45.0; Camera.zoom = 1.0; //观察者位置在z轴负方向的位置上 Camera.eye = {0,0, Camera.zoom * Camera.distance}; //被观察物体的中心坐标 Camera.center = {0.0,0.0,0.0}; //观察者头部朝向y轴方向 Camera.up = {0.0,1.0,0.0}; //依据上述定义,界面初始化后,从界面中看到的坐标系就是以界面中心为原点、水平向右为x轴正半轴、垂直向上为y轴正半轴、屏幕向内为z轴正半轴 } //保证界面内物体的显示不受界面纵横比变化而变形 void GLWidget::resizeGL(int w, int h) { qreal aspect = qreal(w) / qreal(h ? h : 1); const qreal zNear = 0.001, zFar = 1000.0; projection.setToIdentity(); // 得到透视矩阵 projection.perspective(Camera.fovy, aspect, zNear, zFar); } //缩放控制就是控制观察者的位置到被观察物体中心位置的距离,即改变Camera.eye的值 void GLWidget::wheelEvent(QWheelEvent *event) { if(this->m_MousePressFlag) return; if(event->angleDelta().y() > 0) { Camera.zoom -= 0.1; }else{ Camera.zoom += 0.1; } if(Camera.zoom >= 3) { Camera.zoom = 3; }else if(Camera.zoom if(event->button() & Qt::LeftButton) { this->m_MouseFlag = Qt::LeftButton; }else if(event->button() & Qt::RightButton) { this->m_MouseFlag = Qt::RightButton; }else if(event->button() & Qt::MidButton) { this->m_MouseFlag = Qt::MidButton; } this->m_MousePressFlag = true; this->mousePressPosition = event->pos(); } //根据鼠标移动的量和鼠标类型进行对应处理操作 void GLWidget::mouseMoveEvent(QMouseEvent *event) { QPoint diff = event->pos() - mousePressPosition; if(this->m_MouseFlag == Qt::LeftButton) { //按下鼠标左键并移动控制被观察物体绕x轴和y轴进行旋转 this->m_xRot = setRotation(4 * diff.x()); this->m_yRot = setRotation(4 * diff.y()); }else if(this->m_MouseFlag == Qt::RightButton) { //按下鼠标右键并移动控制被观察物体绕x轴和z轴进行旋转 this->m_xRot = setRotation(4 * diff.x()); this->m_zRot = setRotation(4 * diff.y()); }else if(this->m_MouseFlag == Qt::MidButton) { //按下鼠标中键(即滚轮)并移动实现被观察物体的拖拽平移 if(!this->m_MousePressFlag) return; //以下计算是将在界面内移动鼠标的距离投影到被观察物体的xoy平面内,得到物体实际应该移动的距离 qreal w_h_ratio = (qreal)(this->width()) / (qreal)(this->height()); qreal cube_view_height = 2 * Camera.zoom * Camera.distance *qTan(qDegreesToRadians(Camera.fovy/2)); qreal cube_view_width = w_h_ratio * cube_view_height; this->m_xTrans = cube_view_width / qreal(this->width()) * qreal(diff.x()); this->m_yTrans = cube_view_height / qreal(this->height()) * qreal(diff.y()); } update(); } //鼠标释放后,记录当前的旋转或平移矩阵 void GLWidget::mouseReleaseEvent(QMouseEvent *event) { if((event->button() & Qt::LeftButton) || (event->button() & Qt::RightButton)) { this->m_MousePressFlag = false; QMatrix4x4 m; //此处注意,因为欧拉旋转的特性,物体绕三个轴旋转的顺序不同,最终旋转后得到的物体角度是不相同的,所以,每次旋转要保证轴的顺序一致 m.rotate(qreal(this->m_zRot)/16.0f, 0.0f, 0.0f, 1.0f); m.rotate(qreal(this->m_yRot)/16.0f, 0.0f, 1.0f, 0.0f); m.rotate(qreal(this->m_xRot)/16.0f, 1.0f, 0.0f, 0.0f); this->m_rotation = m * this->m_rotation; this->m_xRot = 0; this->m_yRot = 0; this->m_zRot = 0; }else if(event->button() & Qt::MidButton) { this->m_MousePressFlag = false; QMatrix4x4 m; m.translate(this->m_xTrans, -1.0*this->m_yTrans, 0); this->m_translation = m*this->m_translation; this->m_xTrans = 0; this->m_yTrans = 0; } } //编译着色器并连接绑定 void GLWidget::initShaders() { QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); if(!vshader->compileSourceCode(vshader_code)) { qDebug() } int GLWidget::setRotation(int angle) { normalizeAngle(angle); return angle; } void GLWidget::normalizeAngle(int &angle) { while (angle 360 * 16) angle -= 360 * 16; } void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //此处注意,一定是先将物体进行旋转,再进行平移缩放等其他变换 //三维空间中的所有变换都是通过矩阵的左乘来实现的(这部分不知道的可单独查资料学习一下哈),因为旋转矩阵是第一个对物体空间坐标进行左乘处理的! QMatrix4x4 rotation; rotation.rotate(qreal(this->m_zRot)/16.0f, 0.0f, 0.0f, 1.0f); rotation.rotate(qreal(this->m_yRot)/16.0f, 0.0f, 1.0f, 0.0f); rotation.rotate(qreal(this->m_xRot)/16.0f, 1.0f, 0.0f, 0.0f); //计算得到当前旋转矩阵 rotation = rotation * this->m_rotation; QMatrix4x4 m1,m2; //得到当前观察者矩阵 m1.lookAt(Camera.eye, Camera.center, Camera.up); m1 = m1 * rotation; //得到当前平移矩阵 m2.translate(this->m_xTrans, -1.0*this->m_yTrans, 0); m2 = m2 * this->m_translation; program->setUniformValue("mvp_matrix", projection * m2 * m1); this->cubeGeometry->drawCubeGeometry(program); }

源码下载地址:https://download.csdn.net/download/weixin_43493507/87766814?spm=1001.2014.3001.5503

代码部分参考了Qt官方给的例子程序,代码真实可用。有问题的朋友留言评论区,我知道的都会回复的。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3