• Qt OpenGL 光照和键盘控制


    这次教程中,我们将添加光照和键盘控制,它让程序看起来更美观。我将教大家如何使用键盘来移动场景中的对象,还会教大家在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 }

    现在就可以运行程序查看效果了!

  • 相关阅读:
    图解AVL树
    浅析Java7中的ConcurrentHashMap
    浅析CopyOnWriteArrayList
    浅析CAS与AtomicInteger原子类
    IDEA左侧不以树形结构展示项目结构
    maven常见问题
    Mybatis实现多表联合查询
    Mybatis实现单表增删改查操作
    解决mybaits配置错误:Cause: org.xml.sax.SAXParseException; lineNumber: 17; columnNumber: 119; 对实体 "characterEncoding" 的引用必须以 ';' 分隔符结尾。
    Deepin_运维实践系列博客导航
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14048018.html
Copyright © 2020-2023  润新知