• Qt OpenGL 二次几何体


    这次教程中,我将介绍二次几何体。利用二次几何体,我们可以很容易创建球、圆盘、圆柱和圆锥。

    我们先介绍一下二次几何体GLUquadric(NeHe教程用的是GLUquadricObj,源代码中GLUquadricObj是GLUquadric的别名),其实它本质上是一个二次方程,即a1x^2 + a2y^2 + a3z^2 + a4xy + a5yz + a6zx + a7x + a8y + a9z + a10 = 0。要知道,任何一个空间规则曲面(包括平面)都是可以用二次方程表示出来的,因此OpenGL利用二次几何体来实现一些函数,帮助用户更简单的绘画出常用的空间曲面。

    程序运行时效果如下:

    下面进入教程:

    我们将在第07课的基础上修改代码,我只会对新增代码作解释,首先打开myglwidget.h文件,将类声明更改如下:

     1 #ifndef MYGLWIDGET_H
     2 #define MYGLWIDGET_H
     3  
     4 #include <QWidget>
     5 #include <QGLWidget>
     6  
     7 class GLUquadric;
     8 class MyGLWidget : public QGLWidget
     9 {
    10     Q_OBJECT
    11 public:
    12     explicit MyGLWidget(QWidget *parent = 0);
    13     ~MyGLWidget();
    14  
    15 protected:
    16     //对3个纯虚函数的重定义
    17     void initializeGL();
    18     void resizeGL(int w, int h);
    19     void paintGL();
    20  
    21     void keyPressEvent(QKeyEvent *event);           //处理键盘按下事件
    22  
    23 private:
    24     void glDrawCube();                              //绘制立方体
    25  
    26 private:
    27     bool fullscreen;                                //是否全屏显示
    28  
    29     QString m_FileName;                             //图片的路径及文件名
    30     GLuint m_Texture;                               //储存一个纹理
    31  
    32     bool m_Light;                                   //光源的开/关
    33     GLfloat m_xRot;                                 //x旋转角度
    34     GLfloat m_yRot;                                 //y旋转角度
    35     GLfloat m_xSpeed;                               //x旋转速度
    36     GLfloat m_ySpeed;                               //y旋转速度
    37     GLfloat m_Deep;                                 //深入屏幕的距离
    38  
    39     int m_Part1;                                    //圆盘的起始角度
    40     int m_Part2;                                    //圆盘的结束角度
    41     int m_P1;                                       //增量1
    42     int m_P2;                                       //增量2
    43     GLUquadric *m_Quadratic;                        //二次几何体
    44     GLuint m_Object;                                //绘制对象标示符
    45 };
    46  
    47 #endif // MYGLWIDGET_H

    首先我们在类前面增加了GLUquadric的类声明。接着我们增加了6个变量,前4个变量用于控制绘制“部分圆盘”的,下面会解释。然后我们定义一个二次几何体对象指针和一个GLuint变量,二次几何体就不解释了,m_Object是配合键盘控制来完成图形之间切换的。最后我们增加了一个函数声明glDrawCube(),这个函数是用来绘制立方体的。

    接下来,我们需要打开myglwidget.cpp,在构造函数中初始化新增变量(除了m_Quadratic)并修改析构函数(删除掉创建的二次几何体),很简单不多解释,具体代码如下:

     1 MyGLWidget::MyGLWidget(QWidget *parent) :
     2     QGLWidget(parent)
     3 {
     4     fullscreen = false;
     5     m_FileName = "D:/QtOpenGL/QtImage/Wall1.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     m_Part1 = 0;
    15     m_Part2 = 0;
    16     m_P1 = 0;
    17     m_P2 = 1;
    18     m_Object = 0;
    19  
    20     QTimer *timer = new QTimer(this);                   //创建一个定时器
    21     //将定时器的计时信号与updateGL()绑定
    22     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
    23     timer->start(10);                                   //以10ms为一个计时周期
    24 }
    1 MyGLWidget::~MyGLWidget()
    2 {
    3     gluDeleteQuadric(m_Quadratic);
    4 }

    继续,我们需要定义我们新增的glDrawCube()函数了,其实就是画一个纹理立方体,完全可以从第07课的paintGL()函数中复制过来,不再多作解释,代码如下:

     1 void MyGLWidget::glDrawCube()
     2 {
     3     glBegin(GL_QUADS);                                  //开始绘制立方体
     4         glNormal3f(0.0f, 1.0f, 0.0f);
     5         glTexCoord2f(1.0f, 1.0f);
     6         glVertex3f(1.0f, 1.0f, -1.0f);                  //右上(顶面)
     7         glTexCoord2f(0.0f, 1.0f);
     8         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左上(顶面)
     9         glTexCoord2f(0.0f, 0.0f);
    10         glVertex3f(-1.0f, 1.0f, 1.0f);                  //左下(顶面)
    11         glTexCoord2f(1.0f, 0.0f);
    12         glVertex3f(1.0f, 1.0f, 1.0f);                   //右下(顶面)
    13  
    14         glNormal3f(0.0f, -1.0f, 0.0f);
    15         glTexCoord2f(0.0f, 0.0f);
    16         glVertex3f(1.0f, -1.0f, 1.0f);                  //右上(底面)
    17         glTexCoord2f(1.0f, 0.0f);
    18         glVertex3f(-1.0f, -1.0f, 1.0f);                 //左上(底面)
    19         glTexCoord2f(1.0f, 1.0f);
    20         glVertex3f(-1.0f, -1.0f, -1.0f);                //左下(底面)
    21         glTexCoord2f(0.0f, 1.0f);
    22         glVertex3f(1.0f, -1.0f, -1.0f);                 //右下(底面)
    23  
    24         glNormal3f(0.0f, 0.0f, 1.0f);
    25         glTexCoord2f(1.0f, 1.0f);
    26         glVertex3f(1.0f, 1.0f, 1.0f);                   //右上(前面)
    27         glTexCoord2f(0.0f, 1.0f);
    28         glVertex3f(-1.0f, 1.0f, 1.0f);                  //左上(前面)
    29         glTexCoord2f(0.0f, 0.0f);
    30         glVertex3f(-1.0f, -1.0f, 1.0f);                 //左下(前面)
    31         glTexCoord2f(1.0f, 0.0f);
    32         glVertex3f(1.0f, -1.0f, 1.0f);                  //右下(前面)
    33  
    34         glNormal3f(0.0f, 0.0f, -1.0f);
    35         glTexCoord2f(0.0f, 0.0f);
    36         glVertex3f(1.0f, -1.0f, -1.0f);                 //右上(后面)
    37         glTexCoord2f(1.0f, 0.0f);
    38         glVertex3f(-1.0f, -1.0f, -1.0f);                //左上(后面)
    39         glTexCoord2f(1.0f, 1.0f);
    40         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左下(后面)
    41         glTexCoord2f(0.0f, 1.0f);
    42         glVertex3f(1.0f, 1.0f, -1.0f);                  //右下(后面)
    43  
    44         glNormal3f(-1.0f, 0.0f, 0.0f);
    45         glTexCoord2f(1.0f, 1.0f);
    46         glVertex3f(-1.0f, 1.0f, 1.0f);                  //右上(左面)
    47         glTexCoord2f(0.0f, 1.0f);
    48         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左上(左面)
    49         glTexCoord2f(0.0f, 0.0f);
    50         glVertex3f(-1.0f, -1.0f, -1.0f);                //左下(左面)
    51         glTexCoord2f(1.0f, 0.0f);
    52         glVertex3f(-1.0f, -1.0f, 1.0f);                 //右下(左面)
    53  
    54         glNormal3f(1.0f, 0.0f, 0.0f);
    55         glTexCoord2f(1.0f, 1.0f);
    56         glVertex3f(1.0f, 1.0f, -1.0f);                  //右上(右面)
    57         glTexCoord2f(0.0f, 1.0f);
    58         glVertex3f(1.0f, 1.0f, 1.0f);                   //左上(右面)
    59         glTexCoord2f(0.0f, 0.0f);
    60         glVertex3f(1.0f, -1.0f, 1.0f);                  //左下(右面)
    61         glTexCoord2f(1.0f, 0.0f);
    62         glVertex3f(1.0f, -1.0f, -1.0f);                 //右下(右面)
    63     glEnd();                                            //立方体绘制结束
    64 }

    然后我们需要修改一下initializeGL()函数,在其中完成对m_Quadratic的初始化,具体代码如下:

     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     m_Quadratic = gluNewQuadric();                        //创建二次几何体
    15     gluQuadricNormals(m_Quadratic, GLU_SMOOTH);           //使用平滑法线
    16     gluQuadricTexture(m_Quadratic, GL_TRUE);              //使用纹理
    17  
    18     //光源部分
    19     GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f};  //环境光参数
    20     GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};  //漫散光参数
    21     GLfloat LightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f}; //光源位置
    22     glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);     //设置环境光
    23     glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);     //设置漫射光
    24     glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);   //设置光源位置
    25     glEnable(GL_LIGHT1);                                //启动一号光源
    26 }

    注意到我们增加了三行代码,首先调用gluNewQuadric()创建了一个二次几何体对象,并让m_Quadratic指向这个二次几何体。然后第二行代码将在二次曲面的表面创建平滑的法向量,这样当灯光照上去的时候将会好看些。最后我们使在二次曲面表面的纹理映射有效。

    还有就是paintGL()函数了,最近几课,我们通过分过程渐渐让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);                    //移入屏幕5.0单位
     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     switch(m_Object)
    11     {
    12     case 0:                                             //绘制立方体
    13         glDrawCube();
    14         break;
    15     case 1:                                             //绘制圆柱体
    16         glTranslatef(0.0f, 0.0f, -1.5f);
    17         gluCylinder(m_Quadratic, 1.0f, 1.0f, 3.0f, 64, 64);
    18         break;
    19     case 2:                                             //绘制圆盘
    20         gluDisk(m_Quadratic, 0.5f, 1.5f, 64, 64);
    21         break;
    22     case 3:                                             //绘制球
    23         gluSphere(m_Quadratic, 1.3f, 64, 64);
    24         break;
    25     case 4:                                             //绘制圆锥
    26         glTranslatef(0.0f, 0.0f, -1.5f);
    27         gluCylinder(m_Quadratic, 1.0f, 0.0f, 3.0f, 64, 64);
    28         break;
    29     case 5:                                             //绘制部分圆盘
    30         m_Part1 += m_P1;
    31         m_Part2 += m_P2;
    32  
    33         if (m_Part1 > 359)
    34         {
    35             m_P1 = 0;
    36             m_Part1 = 0;
    37             m_P2 = 1;
    38             m_Part2 = 0;
    39         }
    40         if (m_Part2 > 359)
    41         {
    42             m_P1 = 1;
    43             m_P2 = 0;
    44         }
    45  
    46         gluPartialDisk(m_Quadratic, 0.5f, 1.5f, 64, 64, m_Part1, m_Part2-m_Part1);
    47         break;
    48     }
    49  
    50     m_xRot += m_xSpeed;                                 //x轴旋转
    51     m_yRot += m_ySpeed;                                 //y轴旋转
    52 }

    我们将原来的绘制立方体部分的代码换成了一个switch()语句,我们利用m_Object来确定画哪一种物体(具体哪个值对应哪个,请大家参照注释)。我们后面讨论绘制这些物体调用的函数时,会忽略第一个参数m_Quadratic,这个参数将被除立方体外的所有对象使用。由于前面已经解释过二次几何体的实质,我们在讨论下面函数的参数时将忽略它。

    我们创建的第2个对象是一个圆柱体:参数2是圆柱的底面半径;参数3是圆柱的顶面半径;参数4是圆柱的高度(表面我们也可以绘制圆台的);参数5是纬线(环绕z轴有多少细分);参数6是经线(沿着z轴有多少细分)。细分越多该对象就越细致,其实我们可以用gluCylinder来绘制多棱柱的,只要把参数5和参数6换成对应的棱数就行了。

    第3个对象是一个CD一样的盘子:参数2是盘子的内圆半径,该参数可以为0.0,则表示在盘子中间没孔,内圆半径越大孔越大;参数3表示外圆半径,这个参数必须比内圆半径大;参数4是组成该盘子切片的数量;参数5是组成盘子的环的数量,环很像唱片上的轨迹。同样,把参数4改成边数,同样可以得到带孔(不带孔)的多边形。

    第4个对象是球:参数2是球的半径;和圆柱一样,参数3是纬线;参数4是经线。细分越多球看起来就越平滑。

    第5个对象是圆锥:其实和绘制圆柱是一样的,只是把顶面半径设置为0.0,这样顶面就成了一个点。同样参考上面说的方法可以绘制多棱锥。

    第6个对象将被gluPartialDisk()函数创建。相比于gluDisk()函数,gluPartialDisk()多了两个新参数。参数6是我们想要绘制的分部盘子的开始角度,参数6是旋转角,也就是转过的调度。我们将要增加旋转角,这将引起盘子沿顺时针方向缓慢的被绘制在屏幕上。一旦旋转角达到360度,我们将开始增加开始角度,这样盘子看起来就像是被逐渐地抹去一样,我们将重复这两个过程。

    最后我们修改一下键盘控制函数,不多解释了,具体代码如下:

     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_Space:                                 //空格为物体的切换键
    31         m_Object++;
    32         if (m_Object == 6)
    33         {
    34             m_Object = 0;
    35         }
    36         break;
    37     case Qt::Key_PageUp:                                //PageUp按下使木箱移向屏幕内部
    38         m_Deep -= 0.1f;
    39         break;
    40     case Qt::Key_PageDown:                              //PageDown按下使木箱移向观察者
    41         m_Deep += 0.1f;
    42         break;
    43     case Qt::Key_Up:                                    //Up按下减少m_xSpeed
    44         m_xSpeed -= 0.1f;
    45         break;
    46     case Qt::Key_Down:                                  //Down按下增加m_xSpeed
    47         m_xSpeed += 0.1f;
    48         break;
    49     case Qt::Key_Right:                                 //Right按下减少m_ySpeed
    50         m_ySpeed -= 0.1f;
    51         break;
    52     case Qt::Key_Left:                                  //Left按下增加m_ySpeed
    53         m_ySpeed += 0.1f;
    54         break;
    55     }
    56 }

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

  • 相关阅读:
    算法:基于分布的排序算法
    剑指offer:镜像二叉树
    算法:基于比较的排序算法
    LeetCode做题笔记-135
    初识YOLO
    PHP课设图览
    浅谈C语言整型与浮点型转换
    SQL Server EXPRESS 安装
    2020CCPC 东北四省(区域)赛题目一览
    2020CCPC 黑龙江省赛题目一览
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14048486.html
Copyright © 2020-2023  润新知