在Qt中使用OpenGL(二)

前言

在Qt中使用OpenGL(一)
在上一篇文章中,我们结合了一个实际的例子了解了在Qt中使用OpenGL的全部过程。但是肯定对于初次接触的人来说哪怕知道了整个过程,依旧是两眼一抹黑的搞不懂到底要怎么做。
本文章,就从最简的实现来把在Qt中使用OpenGL的全部过程给走一遍。

新建一个窗口类,用于显示

很显然,既然是Qt程序,那么我们首先要做的就是创建一个QWidget。
对于OpenGL,我们就要使用QOpenGLWidget这个类。
同时,对于OpenGL,我们还需要使用它提供的函数,但是OpenGL的版本比较多,每个版本提供的函数也不大一样,Qt帮助我们提供了很多类,方便我们使用。
怎么使用呢?那就是:只要即继承了这个类,那么在这个类的内部,你就可以使用OpenGL的函数。
在这里插入图片描述
看看Qt提供的这些类吧。密密麻麻不是吗?可是我们不需要关心用哪个。
为什么?
因为你现在是一个新手,不需要了解那么多的东西。你只需要知道,我们用一个类就行了:
它提供了
于是,结合之前的我们要创建一个QOpenGLWidget的需求,我们就可以创建出来一个我们自己的窗口了:
在这里插入图片描述
protected:中的三个函数继承自QOpenGLWidget,无论如何你都会用到这三个函数,所以直接重载就行了。
这是一个最简单的窗口,它本身是没有任何功能的。
如果你尝试显示这个Widget,你会看到如下画面:
在这里插入图片描述接下来让我们做一些初始化。

在initializeGL()函数中做一些基本的初始化

首先要做的,是初始化OpenGL函数。
注意,OpenGL本身的API只提供了“函数定义”,所以所有的实现实际上是操作系统或者其它库的工作。
初始化OpenGL函数的目的,就是加载这些OpenGL的实现。
这个操作可以通过以下函数来实现:

initializeOpenGLFunctions();

其次要做的,就是设置一些OpenGL的特性,例如深度测试。
深度测试是指,“近处的物体会遮挡远处的物体”这种在现实中最为基础的法则。
这个操作可以通过以下函数来实现:

glEnable(GL_DEPTH_TEST);

在最后,我们还可以设置一下刷新时的背景颜色是什么。
还记得上一节的那个黑黑的窗口吗?那个颜色就是我们要设置的。
这个操作可以通过以下函数来实现:

glClearColor(0, 0.5, 0.7, 1);

四个参数分别为R,G,B,A,的值,取值范围[0, 1]。

于是,当前的最简单的初始化就是这样:
在这里插入图片描述右侧蓝色的窗口就是我们设置了背景颜色后的结果。
注意,在OpenGL中,颜色值的范围,RGBA每个通道都是从0开始,1结束。如果希望按照[0, 255]的范围来设置,可以用(X / 255.0)进行

创建缓存

众所周知,计算机中进行的一切计算,都需要有输入。
那么,我们绘制3D图像,需要的输入是什么呢?
答案就是顶点。即:用(x,y,z)三个坐标表示的三维的点。
简单来讲,3个点,就可以确认一个三角形。OpenGL的世界中,想要绘制3D图像,你需要的,就是传进去一些顶点。
那么,怎么传入顶点呢?
答案就是将顶点中的x,y,z每个值,一个一个的放到缓存中。
OpenGL中存在两个概念:
一个叫做VAO,一个叫做VBO。
VAO指的是顶点列表对象,VBO指的是顶点缓存对象。
很显然,我们需要将顶点放到缓存,也就是VBO中,但VAO是什么呢?
简单来说,VAO可以帮助我们在绘制多个3D物品时,将各自物品的绘制状态给隔离。即:每个物品都可以有自己的顶点缓存,shader,以及其它的各种各样的状态。VAO会帮你把这些状态保存下来,下一次执行的时候,你就不需要重复的设置这些状态了。简单来说就是:一次设置,到处使用。

VAO对应的Qt中的概念是QOpenGLVertexArrayObject类,VBO对应的Qt中的概念是QOpenGLBuffer
你一定注意到了,VBO并不是什么VertexBufferObject,而就是简简单单的OpenGLBuffer,因为对于OpenGL,顶点缓存和其它的缓存没有什么区别……它都是缓存。
那么,知道了这些之后我们要怎么操作呢?很简单,创建两个对象,然后调用它们的创建函数即可。
当然,不要忘记引用对应的头文件:
在这里插入图片描述在这里插入图片描述
我们将两个对象作为类的成员变量创建了出来,然后在初始化函数中调用了它们的create函数,这样它们就创建好了。
VAO和VBO是互相对应的,你要是希望创建第二个VBO用于绘制第二物体,那也要再创建一个VAO才行。

Shader

最新的OpenGL与之前OpenGL的最大区别,应该就是新版本的OpenGL默认就是动态渲染管线了,于是为了可以控制渲染流程,我们就需要Shader。
对于简单理解就是:
我们如果把顶点数据通过缓存给了OpenGL,那么OpenGL要如何使用这些顶点数据呢?
老版本的OpenGL的使用方法是固定的,我们也无法改变。
但是新版本的OpenGL的使用方法不是固定,并且必须由我们自己来定义。
什么意思?
你给我了三个顶点,那么我要怎么用?
是直接把三个顶点画成一个三角形?还是先来个坐标变换进行缩放,平移,旋转,然后再给每个顶点染个色,最后在画个三角形?这都是用户自己决定的。
在Qt中,提供了QOpenGLShaderProgram类帮助用户来使用shader。
在这里插入图片描述
这里,我们先不要关心shader中的各种语法,而是使用最基础的逻辑:什么都不改变。
即:如果给了我三个顶点,那么就直接用这三个顶点画一个白颜色的三角形。
那么,我们的shader就应该这么写:
在这里插入图片描述
如果要解释一下就是:
shader目前有两个类型,一个类型是vertex,一个类型是fragment
一个用于处理顶点,一个用于处理颜色。
处理顶点的时候需要定义输入的顶点。
处理颜色的时候可以什么都不用定义。

shader的语言与C语言大差不差,很容易理解。

#version 330 core
in vec3 vPos;
void main()
{
    gl_Position = vec4(vPos, 1.0);
}

其中in表示vPos这个变量是输入的变量,类型为vec3即三维向量。
gl_Position表示最终输出的位置,内置的,不需要我们定义,它是一个四维向量,即X,Y,Z,W。
于是很明显,这个shader的含义就是将输入的三维向量最后加载一个1.0的值变成四维向量,然后作为最终的输出位置,即什么都不变。
颜色那边也同样好理解:

#version 330 core
void main()
{
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
} 

gl_FragColor 是表示最终输出的颜色,内置的,不需要我们定义,它是一个四维向量,即R,G,B,A。
于是很明显,这个shader的含义就是最终输出的颜色为(1.0,1.0,1.0,1.0),即白色。
对于shader目前需要了解的就这点了。
最终,我们需要连接一下这个shader,即:

m_program->link();

至此,我们的最简单的shader就准备好了。

使用缓存与Shader

到目前为止,我们的所有操作都是准备。那么,当我们准备好了缓存和shader,下一步就是使用它们了。
我们都知道,OpenGL想要画出一个3D物体,就必须有顶点的输入。OpenGL通过顶点缓存这个概念让用户可以输入顶点信息。
而所谓的顶点缓存,简单点来说,就是一个很简单的,一维数组。例如:

float _vertex[] = {
	 0.0,  0.5, 0.0,
	-0.5, -0.5, 0.0,
	 0.5, -0.5, 0.0,
};

这个数组储存了9个float值,我们可以将其看作三个点,按照x,y,z,x,y,z,x,y,z这个顺序排列。
那么,我们怎么将这9个值,3个点告诉OpenGL呢?即怎么使用VBO呢?
VBO和VAO是匹配出现的,在我们定义好这个三个点后,我们就可以使用VAO和VBO的bind()函数告诉OpenGL我们要用缓存了,即:

m_vao.bind();
m_vbo.bind();

然后,我们将我们自己定义的9个值,3个点绑定到vbo上,即通过VBO的allocate()函数:

m_vbo.allocate(_vertex, 9 * sizeof(float));
// m_vbo.allocate(_vertex, sizeof(_vertex));

这里可以简单的理解一下,就好比我们需要内存保存数据一般,VBO也会创建一块和我们顶点信息相同大小的内存,既然我们的顶点信息是9个float,那这里也就需要使用9个float的大小了。当然,为了方便我们还可以尝试用注释掉的第二种方法。

至此,缓存中已经包含了我们的提供的顶点信息了。
可这就结束了吗?OpenGL就会自动的处理这些顶点信息了吗?
不,OpenGL不会。因为它完全不知道要拿这个缓存中的数据怎么办。
它甚至不知道这里面到底有几个顶点。
不要怀疑,OpenGL真的不知道,万一,你这9个数据,前6个数据代表两个点,后三个数据是垃圾数据没有任何含义怎么办?
是的,这个缓存中所谓的9个数据3个点,是我们自己在大脑中定义的,对于OpenGL来说,它从来没有制定过这么一个标准,也没人告诉它有这么一个标准。
于是,为了让OpenGL能够知道缓存中的数据到底是怎么排列的,我们就需要通过为shader绑定一些信息来告诉OpenGL。
还记得之前我们定义的Shader中,顶点类型的Shader中有一个三维向量的输入吗?它的名字vPos是我们自己定义的,所以我们此时就要使用vPos这个输入变量的名字来告诉OpenGL,我们的顶点缓存中,是按照vPos这个变量的类型,即vec3的标准来保存顶点信息的。
于是我们就可以这么写:

m_program->bind();
m_program->setAttributeBuffer("vPos", GL_FLOAT, 0, 3, 0);
m_program->enableAttributeArray("vPos");

setAttributeBuffer
一般的,当顶点缓存只有顶点信息的时候,setAttributeBuffer()这个函数我们只需要关心前4个参数即可。即shader中的输入参数的名字,顶点缓存中的数据类型,顶点缓存中从第几个点开始才是有用的数据(绝大部分第一个点就是,于是写0即可),顶点缓存中一个点需要用几个数据表示(既要和顶点缓存中的结构匹配,也要和shader中的输入变量的类型匹配,在本例子中为3)。第五个参数一般情况下取0即可。
至此,我们的所有准备工作都完成了。
然后做一个最后的清理工作,释放VAO和Shader:

m_program->release();
m_vao.release();

至此,所有的初始化工作完成。
我们的初始化代码如下图所示:
在这里插入图片描述

开始画三角形吧!

既然我们的准备工作完成了,那么接下来就是绘制三角形了吧。
那么,让我们开始吧,直接上最终的代码,因为本例子中,绘图部分非常的简单:
在这里插入图片描述对,你没看错,代码只有5行,也非常好理解。
分别是启用VAO,启用Shader,用三个点画一个三角形,释放Shader,释放VAO。

至此,一个最简单流程就走完了。
你会发现,我们只使用了void initializeGL()初始化,void paintGL()绘图,但是我们并没有使用void resizeGL(int w, int h)。难道它没用吗?

目前来说的确是没用的……因为我们用了最简单的流程啊……

下一篇:在Qt中使用OpenGL(三)

  • 35
    点赞
  • 121
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
要在QT使用OpenGL渲染一个OBJ模型,可以按照以下步骤进行: 1. 创建一个QT项目,选择OpenGL窗口模板。 2. 在OpenGL窗口初始化OpenGLQT的集成,可以使用以下代码: ```c++ void GLWidget::initializeGL() { initializeOpenGLFunctions(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); } void GLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (double)w / (double)h, 0.01, 100.0); glMatrixMode(GL_MODELVIEW); } void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -5.0f); glRotatef(rotationX, 1.0, 0.0, 0.0); glRotatef(rotationY, 0.0, 1.0, 0.0); drawModel(); } ``` 3. 加载OBJ模型,并将其渲染到OpenGL窗口,可以使用以下代码: ```c++ void GLWidget::loadModel(QString filename) { model = glmReadOBJ(filename.toStdString().c_str()); glmUnitize(model); glmFacetNormals(model); glmVertexNormals(model, 90); } void GLWidget::drawModel() { if (model != NULL) { glmDraw(model, GLM_SMOOTH); } } ``` 4. 在QT的主窗口添加一个QPushButton,点击按钮后调用loadModel()函数加载OBJ模型。 ```c++ void MainWindow::on_btnLoad_clicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), ".", tr("OBJ Files (*.obj)")); if (!filename.isEmpty()) { ui->glWidget->loadModel(filename); } } ``` 5. 运行程序,点击按钮加载OBJ模型并在OpenGL窗口显示。 以上就是在QT使用OpenGL渲染OBJ模型的基本步骤,需要注意的是,这里使用OpenGL Utility Toolkit(GLUT)和OpenGL Mathematics(GLM)库来帮助加载OBJ模型和进行矩阵变换等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值