Qt编程进阶(99):使用OpenGL绘制三维图形
haoteby 2025-06-15 16:22 62 浏览
一、Qt中的OpenGL支持
OpenGL是绘制三维图形的标准API。Qt应用程序可以使用QtOpenGL模块绘制三维图形,该模块依赖于系统的OpenGL库。Qt OpenGL模块提供QGLWidget类,可以通过对它的子类化,并使用OpenGL命令开发出自己的窗口部件。对于许多三维应用程序来说,这就足够了。
从Qt 4开始,可以在QGLWidget上使用QPainter,尽管它还只是一个普通的窗口部件。在QGLWidget上使用QPainter的好处是可以使用OpenGL高效地绘图,例如进行坐标变换以及像素映射绘制。使用QPainter的另一个好处是可以使用其二维的高级API,并结合OpenGL调用来绘制三维图形。
使用QGLWidget可以在场景中使用OpenGL作为后端绘制三维场景。为了在硬件加速的离屏表面上绘制,可以使用pbuffer和framebuffer对象进行扩展,它们分别在QGLPixelBuffer和QGLFramebufferObject类中。
在应用程序中绘制OpenGL是很简单的:派生QGLWidget,实现几个虚函数,连接QtOpenGL和OpenGL库。由于QGLWidget派生自QWidget,许多已知的技术仍然适用。最大的区别是要使用标准的OpenGL函数替代QPainter来实现绘制。
为了使应用程序正确连接QtOpenGL模块和系统的OpenGL库,需要在.pro文件中添加这两项:
QT += opengl
LIBS += -lopengl32 -lglu32
本文介绍一个简单的应用程序,该程序使用这一技术绘制一个三角锥,用户可以使用鼠标与其交互。
二、Tetrahedron类
1.Tetrahedron类的定义
为了介绍QGLWidget的用法,我们编写一个Tetrahedron应用程序。该应用程序可以显示一个三维的三角锥(tetrahedron),或者也可以称为四面体,它的每个面都具有不同的颜色。用户可以通过单击并拖动鼠标来旋转它。用户也可以通过双击某个面,并从弹出的QColorDialog中选择一种颜色来设置这个面的颜色。
class Tetrahedron : public QGLWidget
{
Q_OBJECT
public:
Tetrahedron(QWidget *parent = nullptr);
~Tetrahedron();
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
private:
void draw();
int faceAtPosition(const QPoint &pos);
GLfloat rotationX;
GLfloat rotationY;
GLfloat rotationZ;
QColor faceColors[4];
QPoint lastPos;
};
Tetrahedron类从QGLWidget中派生而来。initializeGL ()、resizeGL()和paintGL()这三个函数是在QGLWidget中实现的。鼠标事件处理函数是在QWidget中实现。
2.OpenGL设置
首先是构造函数:
Tetrahedron::Tetrahedron(QWidget *parent)
: QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
rotationX = -21.0;
rotationY = -57.0;
rotationZ = 0.0;
faceColors[0]=Qt::red;
faceColors[1] = Qt::green;
faceColors[2] = Qt::blue;
faceColors[3] = Qt::yellow;
}
在构造函数中,调用QGLWidget::setFormat()指定OpenGL的显示描述表,并且初始化私有变量。
void Tetrahedron::initializeGL()
{
qglClearColor(Qt::black);
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
initializeGL()函数在调用paintGL()之前只被调用一次。可以在这里设置OpenGL的绘图描述表,定义显示列表,以及执行其他的初始化。
所有的代码都是标准的OpenGL,除了对QGLWidget的qglClearColor()函数的调用。如果想坚持使用标准的OpenGL,则可以在RGBA模式下调用glClearColor(),而在颜色索引模式下调用glClearlndex()。
void Tetrahedron::resizeGL(int w, int h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat x=GLfloat(w)/h;
glFrustum(-x,+x,-1.0,+1.0,4.0,15.0);
glMatrixMode(GL_MODELVIEW);
}
应在第一次调用paintGL()之前,但在initializeGL()之后调用resizeGL()函数。在窗口部件改变大小时也将调用resizeGL()函数。在这里可以设值OpenGL视口、投影以及其他窗口部件尺寸相关的设置。
void Tetrahedron::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw();
}
在窗口部件需要重绘时调用paintGL()函数。它与QWidget::PaintEvent()类似,使用OpenGL函数代替QPainter函数。实际的绘制由私有函数draw()实现。
void Tetrahedron::draw()
{
static const GLfloat P1[3]={0.0,-1.0,+2.0};
static const GLfloat P2[3]={+1.73205081,-1.0,-1.0};
static const GLfloat P3[3]={-1.73205081,-1.0,-1.0};
static const GLfloat P4[3]={0.0,+2.0,0.0};
static const GLfloat * const coords[4][3]={
{ P1,P2,P3}, {P1,P3,P4},{P1,P4,P2},{P2,P4,P3}
};
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-10.0);
glRotatef(rotationX,1.0,0.0,0.0);
glRotatef(rotationY,0.0,1.0,0.0);
glRotatef(rotationZ,0.0,0.0,1.0);
for(int i=0;i<4; ++i){
glLoadName(i);
glBegin(GL_TRIANGLES);
qglColor(faceColors[i]);
for (int j = 0; j < 3; ++j){
glVertex3f(coords[i][j][0],coords[i][j][1],coords[i][j][2]);
}
glEnd();
}
}
在draw()中,我们绘制三角锥,应用x、y和z轴的旋转以及保存在数组中的颜色。每个调用都是标准的OpenGL,除了qglColor()函数。可以根据颜色模式使用OpenGL函数glColor3d()或者glIndex()。
3.事件处理
void Tetrahedron::mouseMoveEvent(QMouseEvent *event)
{
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
if (event->buttons() & Qt::LeftButton) {
rotationX += 180 * dy;
rotationY += 180 * dx;
updateGL();
}else if(event->buttons() & Qt::RightButton) {
rotationX += 180*dy;
rotationZ += 180*dx;
updateGL();
}
lastPos = event->pos();
}
mousePressEvent()和mouseMoveEvent()函数在QWidget中实现,用户可以通过单击并拖动鼠标旋转视图。鼠标左键可以沿着x和y轴旋转,右键可以沿着x和z轴旋转。
当rotationX,rotationY和rotationZ其中一个变量的值改变时,我们调用updateGL()重绘场景。
void Tetrahedron::mouseDoubleClickEvent(QMouseEvent *event)
{
int face = faceAtPosition(event->pos());
if(face != -1) {
QColor color = QColorDialog::getColor(faceColors[face], this);
if(color.isValid()) {
faceColors[face] = color;
updateGL();
}
}
}
mouseDoubleClickEvent()在QWidget中实现,用户可以双击三角锥的面设置其颜色。调用私有函数faceAtPosition()确定光标下的面。如果用户双击了某个面,就调用QColorDialog::getColor()为该面获取一种新的颜色。然后,使用新颜色更新faceColor数组,并且调用updateGL()重绘场景。
int Tetrahedron::faceAtPosition(const QPoint &pos)
{
const int MaxSize = 512;
GLuint buffer[MaxSize];
GLint viewport[4];
makeCurrent();
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(MaxSize, buffer);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(GLdouble(pos.x()), GLdouble(viewport[3]-pos.y()),
5.0, 5.0, viewport);
GLfloat x = GLfloat(width()/height());
glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
draw();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
if(!glRenderMode(GL_RENDER))
return -1;
return buffer[3];
}
faceAtPosition()函数返回窗口部件某位置所处的面的编号,如果没有面就返回-1。确定被选面的代码在OpenGL中有点复杂。事实上,我们以GL_SELECT模式绘制场景,使用OpenGL的选择功能,从OpenGL的命中记录中读取面编号(它的“名字”)。代码是标准的代码,除了起初的QGLWidget::makeCurrent()调用,它可以确保我们正确地使用OpenGL描述表。[QGLWidget在调用initializeGL()、resizeGL()或paintGL()之前会自动调用它,在Tetrahedron实现的其他地方则不需要调用它。]
三、tetrahedron的调用
以下是该应用程序的main()函数:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
if(!QGLFormat::hasOpenGL()){
std::cerr<<"This system has no OpenGL support"<<std::endl;
return 1;
}
Tetrahedron tetrahedron;
tetrahedron.setWindowTitle("Tetrahedron");
tetrahedron.resize(300,300);
tetrahedron.show();
return a.exec();
}
如果用户的系统不支持OpenGL,则会在控制台打印错误信息并立即返回。
这就完成了Tetrahedron应用程序。
——————————————————
对于本文实例完整代码有需要的朋友,可关注并在评论区留言!
相关推荐
- 一日一技:用Python程序将十进制转换为二进制
-
用Python程序将十进制转换为二进制通过将数字连续除以2并以相反顺序打印其余部分,将十进制数转换为二进制。在下面的程序中,我们将学习使用递归函数将十进制数转换为二进制数,代码如下:...
- 十进制转化成二进制你会吗?#数学思维
-
六年级奥赛起跑线:抽屉原理揭秘。同学们好,我是你们的奥耀老师。今天一起来学习奥赛起跑线第三讲二进制计数法。例一:把十进制五十三化成二进制数是多少?首先十进制就是满十进一,二进制就是满二进一。二进制每个...
- 二进制、十进制、八进制和十六进制,它们之间是如何转换的?
-
在学习进制时总会遇到多种进制转换的时候,学会它们之间的转换方法也是必须的,这里分享一下几种进制之间转换的方法,也分享两个好用的转换工具,使用它们能够大幅度的提升你的办公和学习效率,感兴趣的小伙伴记得点...
- c语言-2进制转10进制_c语言 二进制转十进制
-
#include<stdio.h>intmain(){charch;inta=0;...
- 二进制、八进制、十进制和十六进制数制转换
-
一、数制1、什么是数制数制是计数进位的简称。也就是由低位向高位进位计数的方法。2、常用数制计算机中常用的数制有二进制、八进制、十进制和十六进制。...
- 二进制、十进制、八进制、十六进制间的相互转换函数
-
二进制、十进制、八进制、十六进制间的相互转换函数1、输入任意一个十进制的整数,将其分别转换为二进制、八进制、十六进制。2、程序代码如下:#include<iostream>usingna...
- 二进制、八进制、十进制和十六进制等常用数制及其相互转换
-
从大学开始系统的接触计算机专业,到现在已经过去十几年了,今天整理一下基础的进制转换,希望给还在上高中的表妹一个入门的引导,早日熟悉这个行业。一、二进制、八进制、十进制和十六进制是如何定义的?二进制是B...
- 二进制如何转换成十进制?_二进制如何转换成十进制例子图解
-
随着社会的发展,电器维修由继电器时代逐渐被PLC,变频器,触摸屏等工控时代所替代,特别是plc编程,其数据逻辑往往涉及到数制二进制,那么二进制到底是什么呢?它和十进制又有什么区别和联系呢?下面和朋友们...
- 二进制与十进制的相互转换_二进制和十进制之间转换
-
很多同学在刚开始接触计算机语言的时候,都会了解计算机的世界里面大多都是二进制来表达现实世界的任何事物的。当然现实世界的事务有很多很多,就拿最简单的数字,我们经常看到的数字大多都是十进制的形式,例如:我...
- 十进制如何转换为二进制,二进制如何转换为十进制
-
用十进制除以2,除的断的,商用0表示;除不断的,商用1表示余0时结束假如十进制用X表示,用十进制除以2,即x/2除以2后为整数的(除的断的),商用0表示;除以2除不断的,商用1表示除完后的商0或1...
- 十进制数如何转换为二进制数_十进制数如何转换为二进制数举例说明
-
我们经常听到十进制数和二进制数,电脑中也经常使用二进制数来进行计算,但是很多人却不清楚十进制数和二进制数是怎样进行转换的,下面就来看看,十进制数转换为二进制数的方法。正整数转二进制...
- 二进制转化为十进制,你会做吗?一起来试试吧
-
今天孩子问把二进制表示的110101改写成十进制数怎么做呀?,“二进制”简单来说就是“满二进一”,只用0和1共两个数字表示,同理我们平常接触到的“十进制”是“满十进一”,只用0-9共十个数字表示。如果...
- Mac终于能正常打游戏了!苹果正逐渐淘汰Rosetta转译
-
Mac玩家苦转译久矣!WWDC2025苹果正式宣判Rosetta死刑,原生游戏时代终于杀到。Metal4光追和AI插帧技术直接掀桌,连Steam都连夜扛着ARM架构投诚了。看到《赛博朋克2077》...
- 怎么把视频的声音提出来转为音频?音频提取,11款工具实测搞定
-
想把视频里的声音单独保存为音频文件(MP3/AAC/WAV/FLAC)用于配音、播客、听课或二次剪辑?本文挑出10款常用工具,给出实测可复现的操作步骤、优缺点和场景推荐。1)转换猫mp3转换器(操作门...
- 6个mp4格式转换器测评:转换速度与质量并存!
-
MP4视频格式具有兼容性强、视频画质高清、文件体积较小、支持多种编码等特点,适用于网络媒体传播。如果大家想要将非MP4格式的视频转换成MP4的视频格式的话,可以使用MP4格式转换器更换格式。本文分别从...