GLFW入门教程02.05:VAO与VBO
目的
本文介绍使用GLSL绘图。
坐标
想要用GLSL绘图,首先要有对应坐标。
OpenGL显示界面中心点为坐标原点,宽度为[-1,1],高度为[-1,1],不论实际宽高为多少都是这个范围。
数组
将三角形坐标转换为数组。
二维三角形的三个顶点为
1 |
|
这个是以C++数组的形式存储数据,但是此数据发送到GPU中该如何存储呢?
渲染流程
图片来自LearnOpenGL,这个教程比Nehe OpenGL更新更好,推荐。
Vertex
原指(三角形或锥形的)角顶、顶点、至高点,引申为OpenGL中的任一顶点。
绘制一个三角形,给定三个顶点,vertex程序将顶点坐标、纹理坐标传递给fragment程序
在传递时,系统会对顶点组成的区域进行插值,使其形成面。
fragment
原指片元,OpenGL中指的是一片区域。
fragment程序根据插值结果计算最后显示在屏幕上的像素值
VBO
顶点数据数组传递至显卡后,存储在GPU显存上的一段存储空间对象里面,这个对象叫做VBO(Vertex Buffer Object,顶点缓冲对象)。VBO在C++中表现为一个 unsigned int 类型变量,理解成为GPU内存对象的一个ID编号。
VAO
VBO除了表示顶点外,还可以表示颜色。那么问题来了,如果一次渲染多个VBO呢?
VAO(Vertex Array Object,顶点数组对象),用于存储一个Mesh网络所有的顶点属性描述信息。
简单来说,VAO包含一个或多个VBO。
VAO和VBO必须同时出现。
顶点着色器
顶点着色器的作用是把存储在VBO中的顶点数据进行处理并准备绘图。
片段着色器
片段着色器的作用是把已经会好的图进行上色。纹理也是由RGB等颜色组成的,所以纹理填充是在这里实施的。但是没有直接操作片段着色器的接口,所以要把颜色、纹理等通过顶点着色器传递给片段着色器。
fbo/fragment buffer object
FBO与VBO虽然只有一字之差,就它们的意义却大有不同。FBO的主要作用就是改变当前帧缓存的输出路径,除了自身之外,它并不分配内存。默认情况下,显卡的图像数据是输出到帧缓存和深度缓存中去的,帧缓存的数据会直接显示到显示器上。但是用了FBO之后,我们可在改变这一默认的输出方向,把原来要输送到帧缓存或深度缓存的数据输送到一个纹理对像中去,而这个纹理则可以用于后面的运算。
要注意,这里的纹理对像是标准的纹理,要求程序员为其分配显存空间,而FBO只是一个桥梁,起到连接的作用。
pbo/pixel buffer object
PBO设计的目的就是快速地向显卡传输数据,或者从显卡读取数据,我们可以使用它更加高效的读取屏幕数据。
单个PBO读取屏幕数据效率大概和 glReadPixels() 差不多,双PBO交换读取效率会很高。原因是使用PBO时,屏幕上的数据不是读取到内存,而是从显卡读到PBO中,或者如果内部机制是读取到内存中,但这也是由DMA 控制器来完成的,而不是cpu指令来做的,再加上两个PBO交换使用,所以读取效率很高。
还可以使用PBO 高效读取内存数据(比如纹理)到显存中。在没有FBO之前,就是用来它做离屏渲染的。
离屏渲染(offscreen render)
当image buffer需要进行一些额外处理(如圆角、毛玻璃或其他滤镜)并且进行额外处理后无法直接将数据传递至frame buffer进行显示,需要将处理后的数据暂存至offscreen buffer中,再由offscreen buffer传递至frame buffer,最终显示在屏幕上,这个过程就称为离屏渲染。
offscreen buffer同为内存中的一块连续区域。在对图片进行额外处理时用于存放中间合成数据的区域。
渲染流程
flowchart LR
A((.jpeg)) --> B[data buffer]
B --> C[image buffer]
C --> D[frame buffer]
D --> E[display]
离屏渲染流程
flowchart LR
A((.jpeg)) --> B[data buffer]
B --> C[image buffer]
C --> D[offscreen buffer]
D --> E[frame buffer]
E --> F[display]
因此,不一定执行圆角操作(额外处理)就一定会触发离屏渲染,还需要image buffer暂存至offscreen buffer这一过程。
综上,离屏渲染触发条件有两个:
- 图片(图层)需要额外处理
- 数据需要暂存至offscreen buffer
如果图片进行额外处理时导致image buffer暂存至offscreen buffer,那么就会触发离屏渲染。可以理解为,图像额外处理过程较复杂,渲染流水线无法找到单次遍历就能完成渲染的算法,需要暂存中间数据至offscreen buffer,待所有操作处理完成后再传递至frame buffer。
即触发数据需要暂存至offscreen buffer的条件是:渲染流水线无法找到单次遍历就能完成渲染的算法,需要开辟新的内存区域(offscreen buffer)保存中间值。
代码
指定GL版本
首先告诉GLFW当前使用的是OpenGL 3+ API
1 |
|
初始化glad
初始化glad的glfw接口,通过glad我们可以使用几乎所有的OpenGL相关的API。
1 |
|
顶点着色器
顶点着色器是类C代码,以字符串形式提供
1 |
|
片段着色器
片段着色器是类C代码,以字符串形式提供
1 |
|
着色器加载
加载着色器代码,编译代码,检查编译状态,使用着色器
1 |
|
三角形
定义三角形顶点数组
1 |
|
创建VAO和VBO
1 |
|
使用VAO和VBO
1 |
|
先绑定VAO,表示以下的所有VBO都绑定到此VAO中。当然,目前只有一个VBO。
绑定VBO,并将数组内容传递给VBO,但是此时VBO只是知道有一个长度为9数组。
我们还需要告诉VBO如何使用这个数组,即3个一组使用,每个元素都是float。
显示
1 |
|
先告知使用着色器程序,然后使用VAO,开始绘图,绘图模式为轮廓模式。
释放资源
1 |
|
注意这里的释放顺序。
效果
编译运行