Qt+OpenGL教程.docx

上传人:b****5 文档编号:28813566 上传时间:2023-07-19 格式:DOCX 页数:100 大小:1.02MB
下载 相关 举报
Qt+OpenGL教程.docx_第1页
第1页 / 共100页
Qt+OpenGL教程.docx_第2页
第2页 / 共100页
Qt+OpenGL教程.docx_第3页
第3页 / 共100页
Qt+OpenGL教程.docx_第4页
第4页 / 共100页
Qt+OpenGL教程.docx_第5页
第5页 / 共100页
点击查看更多>>
下载资源
资源描述

Qt+OpenGL教程.docx

《Qt+OpenGL教程.docx》由会员分享,可在线阅读,更多相关《Qt+OpenGL教程.docx(100页珍藏版)》请在冰豆网上搜索。

Qt+OpenGL教程.docx

Qt+OpenGL教程

QtOpenGL教程

QtOpenGL的准备工作

第三课:

上色

第四课:

旋转

第一课:

创建一个OpenGL窗口

第二课:

你的第一个多边形

第五课:

向三维进军

第六课:

纹理映射

第七课:

纹理滤波、光源和键盘控制

第八课:

融合

第九课:

在三维空间中移动位图

第十课:

载入一个三维世界并在其中移动

第十一课:

旗的效果(波动纹理)

第十二课:

显示列表

第十三课:

位图字体

第十四课:

轮廓字体

第十五课:

使用纹理映射的轮廓字体

第十六课:

看起来很棒的雾

因为本教程是从NeHe的OpenGL教程迁移过来的,代码变为Qt实现的。

所以有的课程一时还没有实现成功,所以可能有些教程是跳跃的。

因本人时间有限,所以难免有错误出现,如果您发现了这些错误,或者有什么建议,请来信指教,谢谢。

QtOpenGL的准备工作

因为Qt存在很多版本,另外它支持的平台也很多,到目前为止我只实验了几个组合,所以就先把这些列出来吧,欢迎大家补充。

Unix/X11

Linux

Qt:

自由版或者企业版都支持OpenGL模块,而专业版则不能。

我现在使用的是3.1.0自由版和企业版。

gcc:

编译器。

我现在使用的是3.2。

X:

Linux下的图形环境。

我现在使用的是4.2.0。

Mesa:

自由的OpenGL。

我现在使用的是5.0。

Windows

Qt:

企业版支持OpenGL模块,而专业版则不能。

我现在使用的是3.1.0企业版。

MicrosoftVisualStudio:

编译器。

我现在使用的是6.0。

创建一个OpenGL窗口

我假设您对Qt编程已经有了一定的了解,如果您还没有熟悉Qt编程,建议您先学习一下Qt编程的基础知识。

Qt中已经包含了OpenGL模块,具体情况您可以参考QtOpenGL模块的相关内容。

NeHeWidget类

这就是我们继承QGLWidget类得到的OpenGL窗口部件类。

(由nehewidget.h展开。

#include

classNeHeWidget:

publicQGLWidget

{

Q_OBJECT

因为QGLWidget类被包含在qgl.h头文件中,所以我们的类就需要包含这个头文件。

Q_OBJECT是Qt中的一个专用的宏,具体说明请参见Qt的文档。

public:

NeHeWidget(QWidget*parent=0,constchar*name=0,boolfs=false);

~NeHeWidget();

protected:

voidinitializeGL();

voidpaintGL();

voidresizeGL(intwidth,intheight);

因为QGLWidget类已经内置了对OpenGL的处理,就是通过对initializeGL()、paintGL()和resizeGL()这个三个函数实现的,具体情况可以参考QGLWidget类的文档。

因为我们的这个QtOpenGL教程取材于NeHeOpenGL教程,所以这里就用这个NeHeWidget类来继承QGLWidget类来使用相关OpenGL的功能。

initializeGL()是用来初始化这个OpenGL窗口部件的,可以在里面设定一些有关选项。

paintGL()就是用来绘制OpenGL的窗口了,只要有更新发生,这个函数就会被调用。

resizeGL()就是用来处理窗口大小变化这一事件的,width和height就是新的大小状态下的宽和高了,另外resizeGL()在处理完后会自动刷新屏幕。

voidkeyPressEvent(QKeyEvent*e);

这是Qt里面的鼠标按下事件处理函数。

protected:

boolfullscreen;

用来保存窗口是否处于全屏状态的变量。

};

(由nehewidget.cpp展开。

#include"nehewidget.h"

NeHeWidget:

:

NeHeWidget(QWidget*parent,constchar*name,boolfs)

:

QGLWidget(parent,name)

{

fullscreen=fs;

保存窗口是否为全屏的状态。

setGeometry(0,0,640,480);

设置窗口的位置,即左上角为(0,0)点,大小为640*480。

setCaption("NeHe'sOpenGLFramework");

setWindowTitle("NeHe'sOpenGLFramework");

设置窗口的标题为“NeHe'sOpenGLFramework”。

if(fullscreen)

showFullScreen();

如果fullscreen为真,那么就全屏显示这个窗口。

}

这个是构造函数,parent就是父窗口部件的指针,name就是这个窗口部件的名称,fs就是窗口是否最大化。

NeHeWidget:

:

~NeHeWidget()

{

}

这个是析构函数。

voidNeHeWidget:

:

initializeGL()

{

glShadeModel(GL_SMOOTH);

这一行启用smoothshading(阴影平滑)。

阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。

我将在另一个教程中更详细的解释阴影平滑。

GL_FLAT/orGL_SMOOTH

glClearColor(0.0,0.0,0.0,0.0);

这一行设置清除屏幕时所用的颜色。

如果您对色彩的工作原理不清楚的话,我快速解释一下。

色彩值的范围从0.0到1.0。

0.0代表最黑的情况,1.0就是最亮的情况。

glClearColor后的第一个参数是红色,第二个是绿色,第三个是蓝色。

最大值也是1.0,代表特定颜色分量的最亮情况。

最后一个参数是Alpha值。

当它用来清除屏幕的时候,我们不用关心第四个数字。

现在让它为0.0。

我会用另一个教程来解释这个参数。

通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。

希望您在学校里学过这些。

因此,当您使用glClearColor(0.0,0.0,1.0,0.0),您将用亮蓝色来清除屏幕。

如果您用glClearColor(0.5,0.0,0.0,0.0)的话,您将使用中红色来清除屏幕。

不是最亮(1.0),也不是最暗(0.0)。

要得到白色背景,您应该将所有的颜色设成最亮(1.0)。

要黑色背景的话,您该将所有的颜色设为最暗(0.0)。

glClearDepth(1.0);

设置深度缓存。

glEnable(GL_DEPTH_TEST);

启用深度测试。

glDepthFunc(GL_LEQUAL);

所作深度测试的类型。

上面这三行必须做的是关于depthbuffer(深度缓存)的。

将深度缓存设想为屏幕后面的层。

深度缓存不断的对物体进入屏幕内部有多深进行跟踪。

我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。

它的排序决定那个物体先画。

这样您就不会将一个圆形后面的正方形画到圆形上来。

深度缓存是OpenGL十分重要的部分。

glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);

真正精细的透视修正。

这一行告诉OpenGL我们希望进行最好的透视修正。

这会十分轻微的影响性能。

但使得透视图看起来好一点。

}

这个函数中,我们对OpenGL进行所有的设置。

我们设置清除屏幕所用的颜色,打开深度缓存,启用smoothshading(阴影平滑),等等。

这个例程直到OpenGL窗口创建之后才会被调用。

voidNeHeWidget:

:

paintGL()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

清楚屏幕和深度缓存。

glLoadIdentity();

重置当前的模型观察矩阵。

}

这个函数中包括了所有的绘图代码。

任何您所想在屏幕上显示的东东都将在此段代码中出现。

以后的每个教程中我都会在例程的此处增加新的代码。

如果您对OpenGL已经有所了解的话,您可以在glLoadIdentity()调用之后,函数返回之前,试着添加一些OpenGL代码来创建基本的形。

如果您是OpenGL新手,等着我的下个教程。

目前我们所作的全部就是将屏幕清除成我们前面所决定的颜色,清除深度缓存并且重置场景。

我们仍没有绘制任何东东。

voidNeHeWidget:

:

resizeGL(intwidth,intheight)

{

if(height==0)

{

height=1;

}

防止height为0。

glViewport(0,0,(GLint)width,(GLint)height);

重置当前的视口(Viewport)。

glMatrixMode(GL_PROJECTION);

选择投影矩阵。

glLoadIdentity();

重置投影矩阵。

gluPerspective(45.0,(GLfloat)width/(GLfloat)height,0.1,100.0);

建立透视投影矩阵。

glMatrixMode(GL_MODELVIEW);

选择模型观察矩阵。

glLoadIdentity();

重置模型观察矩阵。

}

上面几行为透视图设置屏幕。

意味着越远的东西看起来越小。

这么做创建了一个现实外观的场景。

此处透视按照基于窗口宽度和高度的45度视角来计算。

0.1,100.0是我们在场景中所能绘制深度的起点和终点。

glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projectionmatrix(投影矩阵)。

投影矩阵负责为我们的场景增加透视。

glLoadIdentity()近似于重置。

它将所选的矩阵状态恢复成其原始状态。

调用glLoadIdentity()之后我们为场景设置透视图。

glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响modelviewmatrix(模型观察矩阵)。

模型观察矩阵中存放了我们的物体讯息。

最后我们重置模型观察矩阵。

如果您还不能理解这些术语的含义,请别着急。

在以后的教程里,我会向大家解释。

只要知道如果您想获得一个精彩的透视场景的话,必须这么做。

这个函数的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。

甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次——在程序开始时设置我们的透视图。

OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。

voidNeHeWidget:

:

keyPressEvent(QKeyEvent*e)

{

switch(e->key())

{

caseQt:

:

Key_F2:

fullscreen=!

fullscreen;

if(fullscreen)

{

showFullScreen();

}

else

{

showNormal();

setGeometry(0,0,640,480);

}

updateGL();

break;

如果按下了F2键,那么屏幕是否全屏的状态就切换一次。

然后再根据需要,显示所要的全屏窗口或者普通窗口。

caseQt:

:

Key_Escape:

close();

}

如果按下了Escape键,程序退出。

}

main.cpp

(由main.cpp展开。

#include

#include

Qt的应用程序都是一个QApplication类,所以qapplication.h必须要包含。

因为我们在进入OpenGL窗口之前让用户选择是否使用全屏窗口,所以使用了QMessageBox类,所以qmessagebox.h也要包含。

#include"nehewidget.h"

intmain(intargc,char**argv)

{

boolfs=false;

我们把这个布尔型变量的初始值设置为false。

QApplicationa(argc,argv);

每一个Qt应用程序都使用QApplication类。

switch(QMessageBox:

:

information(0,

"StartFullScreen?

",

"WouldYouLikeToRunInFullscreenMode?

",

QMessageBox:

:

Yes,

QMessageBox:

:

No|QMessageBox:

:

Default))

{

caseQMessageBox:

:

Yes:

fs=true;

break;

caseQMessageBox:

:

No:

fs=false;

break;

}

这里弹出一个消息对话框,让用户选择是否使用全屏模式。

NeHeWidgetw(0,0,fs);

创建一个NeHeWidget对象。

a.setMainWidget(&w);

设置应用程序的主窗口部件为w。

w.show();

显示w。

returna.exec();

程序返回。

}

本课程的源代码。

你的第一个多边形

上一课中,我教您如何创建一个OpenGL窗口。

这一课中,我将教您如何创建三角形和四边形。

我们讲使用GL_TRIANGLES来创建一个三角形,GL_QUADS来创建一个四边形。

我们只要修改第一课中的NeHeWidget类中的paintGL()函数就可以了。

NeHeWidget类

(由nehewidget.cpp展开。

voidNeHeWidget:

:

paintGL()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

清除屏幕和深度缓存。

GlLoadIdentity();

重置当前的模型观察矩阵。

当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。

OpenGL屏幕中心的坐标值是X和Y轴上的0.0点。

中心左面的坐标值是负值,右面是正值。

移向屏幕顶端是正值,移向屏幕底端是负值。

移入屏幕深处是负值,移出屏幕则是正值。

glTranslatef(-1.5,0.0,-6.0);

glTranslatef(x,y,z)沿着X,Y和Z轴移动。

根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0),最后移入屏幕6.0个单位。

注意在glTranslatef(x,y,z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。

现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。

glBegin(GL_TRIANGLES);

开始绘制三角形。

glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd()告诉OpenGL三角形已经创建好了。

通常您会需要画3个顶点,可以使用GL_TRIANGLES。

在绝大多数的显卡上,绘制三角形是相当快速的。

如果要画四个顶点,使用GL_QUADS的话会更方便。

但据我所知,绝大多数的显卡都使用三角形来为对象着色。

最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。

本节的简单示例中,我们只画一个三角形。

如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。

所有六点代码都应包含在glBegin(GL_TRIANGLES)和glEnd()之间。

在他们之间再不会有多余的点出现,也就是说,(GL_TRIANGLES)和glEnd()之间的点都是以三点为一个集合的。

这同样适用于四边形。

如果您知道实在绘制四边形的话,您必须在第一个四点之后,再加上四点为一个集合的点组。

另一方面,多边形可以由任意个顶点,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES)和glEnd()之间有多少行代码。

glVertex3f(0.0,1.0,0.0);

上顶点。

glBegin之后的第一行设置了多边形的第一个顶点,glVertex的第一个参数是X坐标,然后依次是Y坐标和Z坐标。

第一个点是上顶点,然后是左下顶点和右下顶点。

glEnd()告诉OpenGL没有其他点了。

这样将显示一个填充的三角形。

CKer注:

这里要注意的是存在两种不同的坐标变换方式,glTranslatef(x,y,z)中的x,y,z是相对与您当前所在点的位移,但glVertex(x,y,z)是相对于glTranslatef(x,y,z)移动后的新原点的位移。

因而这里可以认为glTranslate移动的是坐标原点,glVertex中的点是相对最新的坐标原点的坐标值。

glVertex3f(-1.0,-1.0,0.0);

左下顶点。

glVertex3f(1.0,-1.0,0.0);

右下顶点。

glEnd();

三角形绘制结束。

glTranslatef(3.0,0.0,0.0);

在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。

为此要再次使用glTranslate。

这次右移,所以X坐标值为正值。

因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。

总共要向右移3.0个单位。

glBegin(GL_QUADS);

开始绘制四边形。

glVertex3f(-1.0,1.0,0.0);

左上顶点。

glVertex3f(1.0,1.0,0.0);

右上顶点。

glVertex3f(1.0,-1.0,0.0);

右下顶点。

glVertex3f(-1.0,-1.0,0.0);

左下顶点。

glEnd();

四边形绘制结束。

}

本课程的源代码。

上色

上一课中我教给您三角形和四边形的绘制方法。

这一课我将教您给三角形和四边形添加两种不同类型的着色方法。

使用单调着色(Flatcoloring)给四边形涂上固定的一种颜色。

使用平滑着色(Smoothcoloring)将三角形的三个顶点的不同颜色混合在一起,创建漂亮的色彩混合。

我们只要修改第二课中的NeHeWidget类中的paintGL()函数就可以了。

NeHeWidget类

(由nehewidget.cpp展开。

voidNeHeWidget:

:

paintGL()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

glTranslatef(-1.5,0.0,-6.0);

glBegin(GL_TRIANGLES);

glColor3f(1.0,0.0,0.0);

红色。

如果您还记得上节课的内容,这段代码在屏幕的左半部分绘制三角形。

这一行代码是我们第一次使用命令glColor3f(r,g,b)。

括号中的三个参数依次是红、绿、蓝三色分量。

取值范围可以从0.0到1.0。

类似于以前所讲的清除屏幕背景命令。

我们将颜色设为红色(纯红色,无绿色,无蓝色)。

glVertex3f(0.0,1.0,0.0);

上顶点。

接下来的一行代码设置三角形的第一个顶点(三角形的上顶点),并使用当前颜色(红色)来绘制。

从现在开始所有的绘制的对象的颜色都是红色,直到我们将红色改变成别的什么颜色。

glColor3f(0.0,1.0,0.0);

绿色。

glVertex3f(-1.0,-1.0,0.0);

左下顶点。

glColor3f(0.0,0.0,1.0);

蓝色。

glVertex3f(1.0,-1.0,0.0);

右下顶点。

glEnd();

glEnd()出现后,三角形将被填充。

但是因为每个顶点有不同的颜色,因此看起来颜色从每个角喷出,并刚好在三角形的中心汇合,三种颜色相互混合。

这就是平滑着色。

glTranslatef(3.0,0.0,0.0);

glColor3f(0.5,0.5,1.0);

一次性将颜色设置为蓝色。

现在我们绘制一个单调着色——蓝色的正方形。

最重要的是要记住,设置当前色之后绘制的所有东东都是当前色的。

以后您所创建的每个工程都要使用颜色。

即便是在完全采用纹理贴图的时候,glColor3f仍旧可以用来调节纹理的色调。

等等...,以后再说吧。

我们必须要做的事只需将颜色一次性的设为我们想采用的颜色(本例采用蓝色),然后绘制场景。

每个顶点都是蓝色的,因为我们没有告诉OpenGL要改变顶点的颜色。

最后的结果是.....全蓝色的正方形。

再说一遍,顺时针绘制的正方形意味着我们所看见的是四边形的背面。

glBegin(GL_QUADS);

glVertex3f(-1.0,1.0,0.0);

glVertex3f(1.0,1.0,0.0);

glVertex3f(1.0,-1.0,0.0);

glVertex3f(-1.0,-1.0,0.0);

glEnd();

}

在这一课中,我试着尽量详细的解释如何为您的OpenGL多边形添加单调和平滑的着色效果的步骤。

改改代码中的红绿蓝分量值,看看最后有什么样的结果。

本课程的源代码。

旋转

上一课中我教给您三角形和四边形的着色。

这一课我将教您如何将这些彩色对象绕着坐标轴旋转。

其实只需在上节课的代码上增加几行就可以了。

我们将在NeHeWidget类中增加两个变量来控制这两个对象的旋转。

它们是浮点类型的变量,使得我们能够非常精确地旋转对象。

浮点数包含小数位置,这意味着我们无需使用1、2、3...的角度。

你会发现浮点数是OpenGL编程的基础。

新变量中叫做rTri的用来旋转三角形,rQuad旋转四边形。

NeHeWidget类

(由nehewidget.h展开。

protected:

boolfullscreen;

GLfloatrTri;

GLfloatrQuad;

};

上面就是添加的两个变量。

rTri是用于三角形的角度,rQuad是用于四边形的角度。

(由nehewidget.cpp展开。

NeHeWidget:

:

NeHeWidget(QWidget*parent,constchar*name,bool

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1