这次教程中,我们将添加光照和键盘控制,它让程序看起来更美观。我将教大家如何使用键盘来移动场景中的对象,还会教大家在OpenGL场景中应用简单的光照,让我们的程序更加视觉效果更好且受我们控制。
程序运行时效果如下:
下面进入教程:
我们这次将在第06课的基础上修改代码,首先打开myglwidget.h文件,将类声明更改如下:
1 #ifndef MYGLWIDGET_H
2 #define MYGLWIDGET_H
3
4 #include <QWidget>
5 #include <QGLWidget>
6
7 class MyGLWidget : public QGLWidget
8 {
9 Q_OBJECT
10 public:
11 explicit MyGLWidget(QWidget *parent = 0);
12 ~MyGLWidget();
13
14 protected:
15 //对3个纯虚函数的重定义
16 void initializeGL();
17 void resizeGL(int w, int h);
18 void paintGL();
19
20 void keyPressEvent(QKeyEvent *event); //处理键盘按下事件
21
22 private:
23 bool fullscreen; //是否全屏显示
24
25 QString m_FileName; //图片的路径及文件名
26 GLuint m_Texture; //储存一个纹理
27 bool m_Light; //光源的开/关
28
29 GLfloat m_xRot; //x旋转角度
30 GLfloat m_yRot; //y旋转角度
31 GLfloat m_xSpeed; //x旋转速度
32 GLfloat m_ySpeed; //y旋转速度
33 GLfloat m_Deep; //深入屏幕的距离
34 };
35
36 #endif // MYGLWIDGET_H
增加了一个布尔变量表示光源的开关,剩下的五个浮点变量用于控制对象的旋转角度,旋转速度以及距离屏幕的位置。
接下来,我们需要打开myglwidget.cpp,加上声明#include <QTimer>,在构造函数中对新增变量(除了m_Texture)进行初始化,同样不作过多解释,代码如下:
1 MyGLWidget::MyGLWidget(QWidget *parent) :
2 QGLWidget(parent)
3 {
4 fullscreen = false;
5 m_FileName = "D:/QtOpenGL/QtImage/Crate.bmp"; //应根据实际存放图片的路径进行修改
6 m_Light = false;
7
8 m_xRot = 0.0f;
9 m_yRot = 0.0f;
10 m_xSpeed = 0.0f;
11 m_ySpeed = 0.0f;
12 m_Deep = -5.0f;
13
14 QTimer *timer = new QTimer(this); //创建一个定时器
15 //将定时器的计时信号与updateGL()绑定
16 connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
17 timer->start(10); //以10ms为一个计时周期
18 }
然后,我们要来添加光照,只需要在initializeGL()函数增加几行代码,具体修改后代码如下:
1 void MyGLWidget::initializeGL() //此处开始对OpenGL进行所以设置
2 {
3 m_Texture = bindTexture(QPixmap(m_FileName)); //载入位图并转换成纹理
4 glEnable(GL_TEXTURE_2D); //启用纹理映射
5
6 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //黑色背景
7 glShadeModel(GL_SMOOTH); //启用阴影平滑
8
9 glClearDepth(1.0); //设置深度缓存
10 glEnable(GL_DEPTH_TEST); //启用深度测试
11 glDepthFunc(GL_LEQUAL); //所作深度测试的类型
12 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //告诉系统对透视进行修正
13
14 GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f}; //环境光参数
15 GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; //漫散光参数
16 GLfloat LightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f}; //光源位置
17 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); //设置环境光
18 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); //设置漫射光
19 glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); //设置光源位置
20 glEnable(GL_LIGHT1); //启动一号光源
21 }
首先我们分别定义环境光参数,漫射光参数以及光源位置。环境光来自于四面八方,所以场景中的对象都处于环境光的照射中;漫射光由特定的光源产生,并在场景中的对象表明产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照到的区域显得要暗一些。这样我们所创建的木板箱的棱边上就会产生很不错的阴影效果。
创建光源的过程和颜色的创建完全一致,前三个参数分别是RGB三色分量,最后一个是alpha通道参数。最后光源位置前三个参数和glTranslate中的一样,一次表示x、y、z轴上的位移,最后一个参数取为1.0f,这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。
接着开始设置光源,使得光源GL_LIGHT1开始发光,然后是设置光源位置(位于木箱原中心在z方向移向观察者2.0单位),最后我们启用一号光源。要注意的是,我们还没有启用GL_LIGHTING,所以是看不见任何光线的。记住,只对光源进行设置、定位、甚至启用,光源都不会工作,除非我们启用GL_LIGHTING。
还有是对paintGL()函数的修改,修改后具体代码如下:
1 void MyGLWidget::paintGL() //从这里开始进行所以的绘制
2 {
3 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
4 glLoadIdentity(); //重置模型观察矩阵
5 glTranslatef(0.0f, 0.0f, m_Deep); //移入屏幕
6 glRotatef(m_xRot, 1.0f, 0.0f, 0.0f); //绕x轴旋转
7 glRotatef(m_yRot, 0.0f, 1.0f, 0.0f); //绕y轴旋转
8
9 glBindTexture(GL_TEXTURE_2D, m_Texture); //选择纹理
10 glBegin(GL_QUADS); //开始绘制立方体
11 glNormal3f(0.0f, 1.0f, 0.0f);
12 glTexCoord2f(1.0f, 1.0f);
13 glVertex3f(1.0f, 1.0f, -1.0f); //右上(顶面)
14 glTexCoord2f(0.0f, 1.0f);
15 glVertex3f(-1.0f, 1.0f, -1.0f); //左上(顶面)
16 glTexCoord2f(0.0f, 0.0f);
17 glVertex3f(-1.0f, 1.0f, 1.0f); //左下(顶面)
18 glTexCoord2f(1.0f, 0.0f);
19 glVertex3f(1.0f, 1.0f, 1.0f); //右下(顶面)
20
21 glNormal3f(0.0f, -1.0f, 0.0f);
22 glTexCoord2f(0.0f, 0.0f);
23 glVertex3f(1.0f, -1.0f, 1.0f); //右上(底面)
24 glTexCoord2f(1.0f, 0.0f);
25 glVertex3f(-1.0f, -1.0f, 1.0f); //左上(底面)
26 glTexCoord2f(1.0f, 1.0f);
27 glVertex3f(-1.0f, -1.0f, -1.0f); //左下(底面)
28 glTexCoord2f(0.0f, 1.0f);
29 glVertex3f(1.0f, -1.0f, -1.0f); //右下(底面)
30
31 glNormal3f(0.0f, 0.0f, 1.0f);
32 glTexCoord2f(1.0f, 1.0f);
33 glVertex3f(1.0f, 1.0f, 1.0f); //右上(前面)
34 glTexCoord2f(0.0f, 1.0f);
35 glVertex3f(-1.0f, 1.0f, 1.0f); //左上(前面)
36 glTexCoord2f(0.0f, 0.0f);
37 glVertex3f(-1.0f, -1.0f, 1.0f); //左下(前面)
38 glTexCoord2f(1.0f, 0.0f);
39 glVertex3f(1.0f, -1.0f, 1.0f); //右下(前面)
40
41 glNormal3f(0.0f, 0.0f, -1.0f);
42 glTexCoord2f(0.0f, 0.0f);
43 glVertex3f(1.0f, -1.0f, -1.0f); //右上(后面)
44 glTexCoord2f(1.0f, 0.0f);
45 glVertex3f(-1.0f, -1.0f, -1.0f); //左上(后面)
46 glTexCoord2f(1.0f, 1.0f);
47 glVertex3f(-1.0f, 1.0f, -1.0f); //左下(后面)
48 glTexCoord2f(0.0f, 1.0f);
49 glVertex3f(1.0f, 1.0f, -1.0f); //右下(后面)
50
51 glNormal3f(-1.0f, 0.0f, 0.0f);
52 glTexCoord2f(1.0f, 1.0f);
53 glVertex3f(-1.0f, 1.0f, 1.0f); //右上(左面)
54 glTexCoord2f(0.0f, 1.0f);
55 glVertex3f(-1.0f, 1.0f, -1.0f); //左上(左面)
56 glTexCoord2f(0.0f, 0.0f);
57 glVertex3f(-1.0f, -1.0f, -1.0f); //左下(左面)
58 glTexCoord2f(1.0f, 0.0f);
59 glVertex3f(-1.0f, -1.0f, 1.0f); //右下(左面)
60
61 glNormal3f(1.0f, 0.0f, 0.0f);
62 glTexCoord2f(1.0f, 1.0f);
63 glVertex3f(1.0f, 1.0f, -1.0f); //右上(右面)
64 glTexCoord2f(0.0f, 1.0f);
65 glVertex3f(1.0f, 1.0f, 1.0f); //左上(右面)
66 glTexCoord2f(0.0f, 0.0f);
67 glVertex3f(1.0f, -1.0f, 1.0f); //左下(右面)
68 glTexCoord2f(1.0f, 0.0f);
69 glVertex3f(1.0f, -1.0f, -1.0f); //右下(右面)
70 glEnd(); //立方体绘制结束
71
72 m_xRot += m_xSpeed; //x轴旋转
73 m_yRot += m_ySpeed; //y轴旋转
74 }
除了旋转及移动上作了修改外(相信大家能看懂),多了glNormal3f()函数的调用。该函数指定一条法线,法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面,如果没有法线,什么怪事情都可能发生:不该亮的面被照亮了,多边形的背面也被照亮了…还要注意的是,法线应指向多边形的外侧。
最后两行代码作了一定的修改,利用变量m_xSpeed、m_ySpeed来控制立方体的旋转速度。
最后当然就是键盘控制了,具体代码如下(相信大家结合注释可以很容易看懂):
1 void MyGLWidget::keyPressEvent(QKeyEvent *event)
2 {
3 switch (event->key())
4 {
5 case Qt::Key_F1: //F1为全屏和普通屏的切换键
6 fullscreen = !fullscreen;
7 if (fullscreen)
8 {
9 showFullScreen();
10 }
11 else
12 {
13 showNormal();
14 }
15 break;
16 case Qt::Key_Escape: //ESC为退出键
17 close();
18 break;
19 case Qt::Key_L: //L为开启关闭光源的切换键
20 m_Light = !m_Light;
21 if (m_Light)
22 {
23 glEnable(GL_LIGHTING); //开启光源
24 }
25 else
26 {
27 glDisable(GL_LIGHTING); //关闭光源
28 }
29 break;
30 case Qt::Key_PageUp: //PageUp按下使木箱移向屏幕内部
31 m_Deep -= 0.1f;
32 break;
33 case Qt::Key_PageDown: //PageDown按下使木箱移向观察者
34 m_Deep += 0.1f;
35 break;
36 case Qt::Key_Up: //Up按下减少m_xSpeed
37 m_xSpeed -= 0.1f;
38 break;
39 case Qt::Key_Down: //Down按下增加m_xSpeed
40 m_xSpeed += 0.1f;
41 break;
42 case Qt::Key_Right: //Right按下减少m_ySpeed
43 m_ySpeed -= 0.1f;
44 break;
45 case Qt::Key_Left: //Left按下增加m_ySpeed
46 m_ySpeed += 0.1f;
47 break;
48 }
49 }
现在就可以运行程序查看效果了!