logo资料库

Qt+Opengl 中文教程.pdf

第1页 / 共80页
第2页 / 共80页
第3页 / 共80页
第4页 / 共80页
第5页 / 共80页
第6页 / 共80页
第7页 / 共80页
第8页 / 共80页
资料共80页,剩余部分请下载后查看
Qt OpenGL 教程 最近一段时间除了学习 Qt,翻译 Qt 文档之外,由于工作和兴趣的原因,开始着 手看 Qt OpenGL 编程。在网上搜索了有关 OpenGL 的教程,发现 NeHe 的 OpenGL 教程的还很不错,作者是 NeHe。上面有很多种语言的实现,但是没有 Qt 和 Gtk 的,所以我就想着手写这个 Qt OpenGL 教程,每课的内容和 NeHe 是一样的。另 外,介绍 NeHe 的一个中文翻译站点 CSDN-CKer 翻译的 NeHe 的 OpenGL 教程, 翻译人是 CKer,在我学习这个教程的过程中,给了我很大的帮助。 下面就是 Qt OpenGL 教程的内容: Qt OpenGL 的准备工作 第一课:创建一个 OpenGL 窗口 第二课:你的第一个多边形 第三课:上色 第四课:旋转 第五课:向三维进军 第六课:纹理映射 第七课:纹理滤波、光源和键盘控制 第八课:融合 第九课:在三维空间中移动位图 第十课:载入一个三维世界并在其中移动
第十一课:旗的效果(波动纹理) 第十二课:显示列表 第十三课:位图字体 第十四课:轮廓字体 第十五课:使用纹理映射的轮廓字体 第十六课:看起来很棒的雾 因为本教程是从 NeHe 的 OpenGL 教程迁移过来的,代码变为 Qt 实现的。所以 有的课程一时还没有实现成功,所以可能有些教程是跳跃的。 因本人时间有限,所以难免有错误出现,如果您发现了这些错误,或者有什么建 议,请来信指教,谢谢。 Qt OpenGL 的准备工作 因为 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 企业版。 Microsoft Visual Studio:编译器。我现在使用的是 6.0。 创建一个 OpenGL 窗口 我假设您对 Qt 编程已经有了一定的了解,如果您还没有熟悉 Qt 编程,建议您先 学习一下 Qt 编程的基础知识。 Qt 中已经包含了 OpenGL 模块,具体情况您可以参考 Qt OpenGL 模块的相关内 容。
NeHeWidget 类 这就是我们继承 QGLWidget 类得到的 OpenGL 窗口部件类。 (由 nehewidget.h 展开。) #include class NeHeWidget : public QGLWidget { Q_OBJECT 因为 QGLWidget 类被包含在 qgl.h 头文件中,所以我们的类就需要包含这个头文 件。Q_OBJECT 是 Qt 中的一个专用的宏,具体说明请参见 Qt 的文档。 public: NeHeWidget( QWidget* parent = 0, const char* name = 0, bool fs = false ); ~NeHeWidget(); protected: void initializeGL(); void paintGL(); void resizeGL( int width, int height ); 因为 QGLWidget 类已经内置了对 OpenGL 的处理,就是通过对 initializeGL()、 paintGL()和 resizeGL()这个三个函数实现的,具体情况可以参考 QGLWidget 类的 文档。 因为我们的这个 Qt OpenGL 教程取材于 NeHe OpenGL 教程,所以这里就用这个 NeHeWidget 类来继承 QGLWidget 类来使用相关 OpenGL 的功能。 initializeGL()是用来初始化这个 OpenGL 窗口部件的,可以在里面设定一些有关 选项。paintGL()就是用来绘制 OpenGL 的窗口了,只要有更新发生,这个函数就 会被调用。resizeGL()就是用来处理窗口大小变化这一事件的,width 和 height 就 是新的大小状态下的宽和高了,另外 resizeGL()在处理完后会自动刷新屏幕。 void keyPressEvent( QKeyEvent *e ); 这是 Qt 里面的鼠标按下事件处理函数。 protected:
bool fullscreen; 用来保存窗口是否处于全屏状态的变量。 }; (由 nehewidget.cpp 展开。) #include "nehewidget.h" NeHeWidget::NeHeWidget( QWidget* parent, const char* name, bool fs ) : QGLWidget( parent, name ) { fullscreen = fs; 保存窗口是否为全屏的状态。 setGeometry( 0, 0, 640, 480 ); 设置窗口的位置,即左上角为(0,0)点,大小为 640*480。 setCaption( "NeHe's OpenGL Framework" ); 设置窗口的标题为“NeHe's OpenGL Framework”。 if ( fullscreen ) showFullScreen(); 如果 fullscreen 为真,那么就全屏显示这个窗口。 } 这个是构造函数,parent 就是父窗口部件的指针,name 就是这个窗口部件的名 称,fs 就是窗口是否最大化。 NeHeWidget::~NeHeWidget() { } 这个是析构函数。 void NeHeWidget::initializeGL() { glShadeModel( GL_SMOOTH );
这一行启用 smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩, 并对外部光进行平滑。我将在另一个教程中更详细的解释阴影平滑。 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 ); 所作深度测试的类型。 上面这三行必须做的是关于 depth buffer(深度缓存)的。将深度缓存设想为屏 幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。我们本节的 程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示 3D 场景 OpenGL 程 序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面 的正方形画到圆形上来。深度缓存是 OpenGL 十分重要的部分。 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); 真正精细的透视修正。这一行告诉 OpenGL 我们希望进行最好的透视修正。这会 十分轻微的影响性能。但使得透视图看起来好一点。 } 这个函数中,我们对 OpenGL 进行所有的设置。我们设置清除屏幕所用的颜色, 打开深度缓存,启用 smooth shading(阴影平滑),等等。这个例程直到 OpenGL 窗口创建之后才会被调用。
void NeHeWidget::paintGL() { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 清楚屏幕和深度缓存。 glLoadIdentity(); 重置当前的模型观察矩阵。 } 这个函数中包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段 代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。如果您对 OpenGL 已经有所了解的话,您可以在 glLoadIdentity()调用之后,函数返回之前, 试着添加一些 OpenGL 代码来创建基本的形。如果您是 OpenGL 新手,等着我的 下个教程。目前我们所作的全部就是将屏幕清除成我们前面所决定的颜色,清除 深度缓存并且重置场景。我们仍没有绘制任何东东。 void NeHeWidget::resizeGL( int width, int height ) { 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)指明接下来的两行代码将影响 projection matrix (投影矩阵)。投影矩阵负责为我们的场景增加透视。 glLoadIdentity()近似于 重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为 场景设置透视图。 glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix (模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型 观察矩阵。如果您还不能理解这些术语的含义,请别着急。在以后的教程里,我 会向大家解释。只要知道如果您想获得一个精彩的透视场景的话,必须这么做。 这个函数的作用是重新设置 OpenGL 场景的大小,而不管窗口的大小是否已经改 变(假定您没有使用全屏模式)。甚至您无法改变窗口的大小时(例如您在全屏 模式下),它至少仍将运行一次——在程序开始时设置我们的透视图。OpenGL 场景的尺寸将被设置成它显示时所在窗口的大小。 void NeHeWidget::keyPressEvent( QKeyEvent *e ) { switch ( e->key() ) { case Qt::Key_F2: fullscreen = !fullscreen; if ( fullscreen ) { showFullScreen(); } else { showNormal(); setGeometry( 0, 0, 640, 480 ); } updateGL(); break;
分享到:
收藏