Qt创建一个OpenGL窗口

本文详细介绍了如何在Qt5.12环境下结合OpenGL进行三维模型开发,覆盖了环境配置、代码实现及运行效果,适合初学者快速入门。

点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,可以点个在看,让它可以帮助到更多同志~

一直以来想结合Qt学习OpenGL,但是自己查了一些资料后还是比较困惑,最近在网上找到了两个资料链接,一个是NeHe的教程,相对来讲旧一些;另一个是https://learnopengl.com/。我的目的是在Qt开发过程中,如果需要用到OpenGL相关的三维模型开发,可以快速上手,所以我选择的是用Qt比较新的版本自己做一遍。至于内部图形学 原理,则是边写程序边学习,所以本教程比较适合初学者。在查看网上一些教程之后,大部分还是以前的Qt版本,所以打算用比较新的版本来改写。

 

环境:Win10、 Qt 5.12 、MinGW

 

效果预览:

1. 新建继承QWidget的类MyGLWidget

之后进行如下改写:

#include <QOpenGLWidget>


class MyGLWidget : public QOpenGLWidget
{
    Q_OBJECT


public:
    MyGLWidget(QWidget *parent = nullptr,
               bool windowFlag = false);


    ~MyGLWidget();


protected:


  void initializeGL();
  void paintGL();
  void resizeGL( int width, int height );


  void keyPressEvent( QKeyEvent *e );


protected:


  bool fullscreen;     //窗口是否处于全屏状态
};

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

https://doc.qt.io/qt-5/qopenglwidget.html#details

 

initializeGL()是用来初始化这个OpenGL窗口部件的,可以在里面设定一些有关选项。paintGL()就是用来绘制OpenGL的窗口了,只要有更新发生,这个函数就会被调用。resizeGL()就是用来处理窗口大小变化这一事件的,width和height就是新的大小状态下的宽和高了,另外resizeGL()在处理完后会自动刷新屏幕。

 

2. pro文件改写

Qt MinGW编译工具下需要链接库 opengl32 和 glu32

pro中添加如下:

win32-g++ {
    LIBS += -lopengl32 -lglu32
}

我的Windows系统已经带了opengl32库和glu32库,如果没有的话需要去下载。

 

如果不链接opengl相关库,编译时会报出下面的错误

3. 各个函数实现

①头文件的包含

#include <GL/glu.h>

#include <QKeyEvent>

 

②函数实现

MyGLWidget::MyGLWidget(QWidget *parent,
                       bool windowFlag)
    : QOpenGLWidget(parent)
{
    fullscreen = windowFlag;
    setGeometry( 0, 0, 640, 480 ); //设置窗口的位置,即左上角为(0,0)点,大小为640*480
    //设置窗口的标题为“ goose's OpenGL Framework”
    setWindowTitle( "A goose's OpenGL Framework" ); 


    if ( fullscreen )
      showFullScreen();
}

void MyGLWidget::initializeGL()

{

//启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。我将在另一个教程中更详细的解释阴影平滑。

glShadeModel( GL_SMOOTH );

//这一行设置清除屏幕时所用的颜色。如果对色彩的工作原理不清楚的话,这里简单说明下。色彩值的范围从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)。

glClearColor( 0.0, 0.0, 0.0, 0.0 );

glClearDepth( 1.0 );         //设置深度缓存

 

glEnable( GL_DEPTH_TEST );    //启用深度测试

 

glDepthFunc( GL_LEQUAL );    //所作深度测试的类型

 

//上面这三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。

 

//真正精细的透视修正。这一行告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。

    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); 

}

//这个函数中,我们对OpenGL进行所有的设置。我们设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。

void MyGLWidget::paintGL()
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); //清楚屏幕和深度缓存
    glLoadIdentity();                                 //重置当前的模型观察矩阵
}

//这个函数中包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,函数返回之前,试着添加一些OpenGL代码来创建基本的形。如果您是OpenGL新手,等着我的下个教程。目前我们所做的全部就是将屏幕清除成我们前面所决定的颜色,清除深度缓存并且重置场景。我们仍没有绘制任何东西

void MyGLWidget::resizeGL(int width, int height)
{
    if ( height == 0 )   //防止height为0
    {
      height = 1;
    }
    glViewport( 0, 0, GLint(width), GLint(height) ); //重置当前的视口(Viewport)
    glMatrixMode( GL_PROJECTION );                   //选择投影矩阵
    glLoadIdentity();                                //重置投影矩阵
    gluPerspective( 45.0, GLdouble(width)/GLdouble(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 MyGLWidget::keyPressEvent(QKeyEvent *e)
{
    switch ( e->key() )
{
//如果按下了F2键,那么屏幕是否全屏的状态就切换一次。然后再根据需要,显示所要的全屏窗口
//者普通窗口。
    case Qt::Key_F2:   
      fullscreen = !fullscreen;
      if ( fullscreen )
      {
        showFullScreen();
      }
      else
      {
        showNormal();
        setGeometry( 0, 0, 640, 480 );
      }
      update();
      break;


//如果按下了Escape(Esc)键,程序退出
    case Qt::Key_Escape:
      close();
    }
}

4.  main函数设置

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


    bool fs = false;      //是否全屏为false


//这里弹出一个消息对话框,让用户选择是否使用全屏模式
    switch( QMessageBox::information( nullptr,
          "Start FullScreen?",
          "Would You Like To Run In Fullscreen Mode?",
          QMessageBox::Yes,
          QMessageBox::No | QMessageBox::Default ) )
      {
      case QMessageBox::Yes:
        fs = true;
        break;
      case QMessageBox::No:
        fs = false;
        break;
      }


//创建一个MyGLWidget对象
    MyGLWidget w(nullptr,fs);
    w.show();


    return a.exec();            //开启Qt的事件循环
}

5. 程序运行效果

①对话框界面,选择No

②运行效果

屏幕左上角

6. 小结

 

主要是对别人写好的程序做了Qt版本的提升;

这里面很多术语我也不懂,无法建立起一个完整的框架,我是打算边写程序边建立框架;

对于程序中 文字解释的排版欢迎大家多提出宝贵的意见,感谢!

最后,如需程序源码可在公众号后台留言

学不可以已

创建一个OpenGL窗口: 在这个教程里,我将教你在Windows环境中创建OpenGL程序.它将显示一个空的OpenGL窗口,可以在窗口和全屏模式下切换,按ESC退出.它是我们以后应用程序的框架. 理解OpenGL如何工作非常重要,你可以在教程的末尾下载源程序,但我强烈建议你至少读一遍教程,然后再开始编程. 2.你的第一个多边形: 在第一个教程的基础上,我们添加一个三角形和一个四边形。也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形。 读完了这一课,你会学到如何在空间放置模型,并且会知道深度缓存的概念。 3.添加颜色: 作为第二课的扩展,我将叫你如何使用颜色。你将理解两种着色模式,在左图中,三角形用的是光滑着色,四边形用的是平面着色。 注意三角形上的颜色是如何混合的。 颜色为OpenGlL 工程增加很多。通过理解平面着色(flat coloring)和平滑着色(smooth coloring),你能显著的改善你的OpenGL Demo的样子。 4.旋转: 在这一课里,我将教会你如何旋转三角形和四边形。左图中的三角形沿Y轴旋转,四边形沿着X 轴旋转。 这一章将引入两个变量, rtri 被用来存储三角形的角度, rquad存储四边形的角度。 和容易创建一个多边形组成的场景。让这些物体动起来是整个场景变得生动起来。在后面的课程钟我将教给你如何绕屏幕上的一个点旋转物体,使得物体绕屏幕而不是它的轴转动。 5.3D形体: 既然我们已经领会到多边形,方形,色彩和旋转。现在该建立3D物体了。我将使用多边形和矩形c创建3D物体。这次我们将扩展上一章的教程,并且将三角形转换成一个彩色的棱锥,把正方形变为一个实心正方体。棱锥使用混合色,正方体每个面使用一种颜色。在3D空间创建物体可能很费时间,但是所获得的结果(收获)值得这样做。充分发挥你的想象力吧。 6.纹理映射: 你想要它,它现在就在这里了,那就是 ... 纹理映射!!!在这一章我将教会你如何将一幅位图(bitmap)映射到正方体的六个面上去。我们将使用第一章的OpenGL代码来创建工程。创建一个空的窗口比修改上一课的代码更容易。 你将会发现第一章的代码在对于快速创建工程来说是及其有价值的。第一章的代码为你设置好了一切,你所需要做的只是集中精力为效果编程。 7.纹理滤波, 光照和键盘控制: 好的,我希望到现在你已经理解了所有的东西,因为这是一个巨大的教程。我想教给你两个新的方法来过滤(filter)你的纹理,简单的光照,键盘控制并且还可能更多 :) .如果你对到这一课为止你所学的东西并不充满信心,那就回头复习一下。玩一下其它课程的代码,不要操之过急。最好专心把每一课学好,而不是蜻蜓点水,只知道如何把东西做出来。 8.混合 有理由等一下,一个来自很酷的Hypercosm的程序员伙伴问(我)他是否可以写一章关于混合的教程。第八课通常正是讲混合的,所以太巧了。这一章教程扩展了第七章。混合是一项很酷的技术 .. 我希望你们能好好享受这一章教程。这一章的作者是Tom Stanis他在这制作一章上花费了很多精力,所以让他知道你觉得怎么样。混合可不是一个好讲的话题。 9.在3D空间中移动位图: 这一章覆盖了一些你们要求的主题,你想知道如何移动你在3D屏幕空间上创造的物体。你想要知道如何在屏幕上绘制一幅位图,并且位图的黑色部分不会覆盖它后面的东西。你想要简单的动画,想要更多的混合的应用,这一章将教会你所有这些。You'll notice there's no spinning boxes(yaker:很惭愧这一句我不是很明白)。前面的课程覆盖了OpenGL的基础,每一章都基于前面的内容。前面的课程涵盖了基础的OpenGL,每一课都是在前一课的基础上创建的。这一课是前面几课知识的综合,当你学习这课时,请确保你已经掌握了前面几课的知识。 10.加载3D世界,并在其中漫游: 你一直期待的教程来了!这一章友一个叫Lionel Brites的伙伴制作。这一课里你讲学到如何导入一个3D世界。代码仍然使用第一章的,但是,课程页面只是解释了新的部分,包括导入3D场景,在3D世界中移动。下载VC++代码并且在你阅读教程的同时阅读代码。按[B]键控制混合,[F]键控制滤波,[L]键控制光照(但光并不随场景移动),还有[Page UP]和[Page Down]键。我希望你能喜欢Lionel对网站的贡献。我有空的时候我会让这个教程更容易学习。 11.旗帜效果
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值