本文主要解决一个问题:
如何渲染一个三角形?
本章中,会有大量的新名词和解释,大量的函数出现,建议找个安静的地方慢慢啃这块骨头。
首先,先从直觉上来想想要渲染一个三角形我们需要做些什么?大概需要这三个步骤:
我们就从这几个方面来画出我们的三角形
在OpenGL中,所有的顶点都是三维空间内的顶点,不过这不是问题,我们可以把深度定义为0来保证其在一个平面上。于是,我们定义三个点:(-0.5, 0.5, 0.0),(0.5, -0.5, 0.0),(0.0, 0.5, 0.0),由于在OpenGL中所有的顶点都会被规范化(将顶点的位置变换到-1到1之间),我们就直接定义规范化后的点,方便处理。我们的代码就像这个样子(编程是一种手写编码的过程,不是Ctrl+C、Ctrl+V的过程,手写一遍代码还是非常必要的):
我们有了顶点数组,如何让OpenGL知道呢?这里有个新东西,叫顶点缓存对象(Vertex Buffer Object,简称VBO),表示存储在GPU显存中的大量顶点数据。我们可以通过这个对象,一次性向GPU发送大量的数据,而不是一次次地从CPU中发送数据到GPU,这是个很慢的过程。
唯一ID:OpenGL中有太多的东西,我们需要一个唯一的ID,就像身份证一样来标识出哪个是哪个。这个ID不能我们自己来定,只能告诉OpenGL说,我需要一个唯一ID,你给我一个吧。然后,OpenGL就会给你一个没用过的唯一ID,这个过程是由glGenBuffers来实现的。
指明缓存对象类型:OpenGL中有很多缓存对象,虽然它给了我们一个ID,但不知道这个ID是用来表示什么缓存对象的。我们要明确告诉它,这是一个顶点缓存对象。绑定的作用,是将原来有的东西替换成我们新的东西,这样,接下来对GL_ARRAY_BUFFER的操作都是对我们新东西的操作了。
将顶点数据传到VBO:
从OpenGL 3.3开始,OpenGL的渲染方式就从固定功能管线转向了可编程功能管线。而可编程的意思,就是可以通过许许多多的着色器来实现多种多样的效果,这些效果要比固定的效果好的多。顶点着色器就是这些许许多多的着色器中的一种。
先放出一段代码(GLSL语言编写,语法与C极为类似):
gl_Position是GLSL内置变量,GPU将从这个位置读取数据并显示点线.
代码自然是要编译链接之后才能执行的,这里我们先说编译,链接的部分等讲完片元着色器再一起说,先不用纠结。
首先,创建一个着色器对象,返回值是这个对象的唯一ID
然后,我们将源码附加到着色器对象上并且编译这个着色器对象
同样,先放出代码
这里FragColor是GLSL内置的一个变量,用于指定上述定点内部的颜色.
类似的,我们输出一个颜色供片元使用,这个颜色是橙色。一个片元,包含了OpenGL用来渲染一个像素的所有信息。
我们同样需要创建一个着色器对象,然后将源码附加到着色器对象上,然后编译。代码如下:
着色器程序对象是最终将所有着色器连接起来的对象。把所有的着色器对象和这个着色器程序对象连接起来之后,我们就能使用这个着色器程序对象了。
将着色器都连接到程序对象上时,OpenGL会将上一个阶段的输出连接到下一个阶段的输入上。
我们需要三步来使用着色器程序对象,现在已经很熟悉了。创建、附加、链接。
代码很简单,只有一行
将已经附加过的着色器都清除掉,我们现在不需要它们了(过河拆桥???)
顶点着色器给了我们极大的便利性,我们可以输入想要输入的任何属性格式。因此,我们也就要告诉OpenGL我们顶点的格式是什么样子的。我们定义的顶点格式如下:
可以看到一些重要信息,我们的起始顶点的偏移为0,每个位置分量占用4字节的空间,一个顶点占用12字节空间。
于是,我们调用glVertexAttribPointer函数来指定顶点格式。
我们获取的顶点属性是由VBO决定的,而glVertexAttribPointer操作的是当前绑定到GL_ARRAY_BUFFER上的VBO,所以,我们当前操作的就是我们之前生成并绑定的那个VBO。
glEnableVertexAttribArray(0)是用来让顶点属性生效的,参数0就是我们之前配置的那个顶点属性的位置。
OpenGL中并没有将我们上面设想的两步单独弄出来,只需要指明要画的是三角形,而且是实体三角形,这两步就能自动完成了。
所以,当我们做完上面那么多步骤之后,我们只需要调用一行代码就可以了。
到这里,似乎我们要做的事情都已经做完,只要编译运行代码就能看到我们想要的三角形了。然而,事实并不像我们想象的那样。运行的代码还是一片青灰色,根本看不到三角形的影子。加了一个VAO进去之后,就能显示出三角形来了。看看教程,原来这个VAO已经是OpenGL渲染管线中不可缺少的一部分了。所以,我们要郑重其事地来了解一下VAO。
全称是顶点数组对象(Vertex Array Object),作用是来保存对顶点属性的调用。这样,当我们需要这些顶点属性的时候,只需要简单地绑定VAO,不需要再设置一遍顶点属性就可以进行绘制了。(是不是觉得没有必要调用,我不想保存,只想显示。所以笔者才尝试不用VAO看看能不能显示出三角形,结果是,没戏,只能乖乖的用它。)
VAO会保存两种东西:
其一、对glEnableVertexAttribArray或者是DisableVertexAttribArray的调用。
其二、使用glVertexAttribPointer设置的顶点属性以及与顶点属性相关连的VBO。
生成VAO并绑定的代码如下:
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
将这段代码添加到生成VBO的代码之后就可以编译运行了。
嗯,不出所料,我们的三角形显示出来了。
参考代码:https://gitee.com/pengrui2009/open-gl-study/blob/master/brave_world.cpp
这是一张顶点处理的流程图,我们所做的工作就是处理其中的一些阶段。今天我们处理的是顶点着色和片元着色,之后在学光照和纹理的时候我们依旧是处理这些着色,可见这两个阶段是多么重要。
本章节工程代码:
https://gitee.com/pengrui2009/open-gl-study/tree/master/chapter2
原文链接
作者:闪电的蓝熊猫
链接:https://www.jianshu.com/p/f9b1162e62cf