OpenGL超级宝典(第7版)笔记19 用缓冲为VS提供数据 清单5.1-5.8
文章目录
前言
上一篇我们把向量、矩阵、摄像机的类做了介绍,并且实现了3D下的移动。但是我们遇到了一个比较麻烦的事情,就是我们在输入顶点数据的时候,仍然采用将数据内置到shader中并用gl_VertexID来调用,非常的麻烦。如果我们有建模软件得到的模型(可能包含成千上万的三角面),我们不能都把它弄到shader中去,太麻烦了。
这回我们将通过建立缓存,并把缓冲中的数据自动的填充到shader中去,实现大量顶点的输入。
1 具体的思路
首先我们要学习怎样建立缓冲区,怎样设置缓冲区大小并初始化,怎样修改缓冲区中的数据(直接修改、通过指针修改),怎样实现缓冲区之间的数据拷贝,怎样把缓冲区中的数据填充到顶点着色器的顶点属性中。
涉及到的清单为5.1-5.8
先放一个函数的流程图:
我们将慢慢带大家把这个流程走一遍,大家也在看完之后可以在回到这个流程图,看看这些内容是否都理解了。(先提示一下这节比较难的是两个,一是glVertexArrayAttribFormat解释缓冲区的数据结构,二是glVertexArrayAttribBinding主要是VAO中的各种绑定,这里很容易乱)
2 创建缓冲区并进行初始化 清单5.1
我们首先要创建一个缓冲区,使用glCreateBuffers来创建,可以同时创建多个缓冲区。glCreateBuffers通过指针传回的是缓冲区的一串数字,用于代表该缓冲区的名字(其本事并不是缓冲区的指针,OpenGL把这个名字的值和缓冲区本身做了分离,不像我们自己的数组,数组名字就代表了指向数据的指针),你可以把这个数字看成是缓冲区的编号。
创建完缓冲区后我们要对缓冲区进行初始化(指定大小,并初始化数据)。可以有两种方法:
一是通过缓冲区的编号来对缓冲区做初始化,即glNamedBufferStorage,
二是对指定的绑定点上绑定的缓冲区做初始化,即glBufferStorage。注意需要先用glBindBuffer把需要初始化的缓冲区绑定在特定的绑定点上。
(打个比方:第一种就是把数据送给叫张三的人,只可能给到张三不会给到其他人。而第二种是把数据送给绑在第X根柱子上的人,绑在第X根柱子上的人可能是张三也可能是其他人,就看谁在那之前被绑在了第X根柱子上)
比如我下面就通过两种方式分别把两个缓冲区进行了初始化:
GLuint vertex_buffer[2];
glCreateBuffers(2,&vertex_buffer[0]);
//这时候就生成了两个缓冲区对象,分别是vertex_buffer[0]和vertex_buffer[1]
glNamedBufferStorage(vertex_buffer[1], 128 * 128, NULL, GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT);
//这里直接通过缓冲区对象进行初始化
//注意这里是|而不是||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer[0]);
glBufferStorage(GL_ARRAY_BUFFER, 128 * 128, NULL, GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT);
//这里通过先把vertex_buffer[0]绑定在绑定点GL_ARRAY_BUFFER上
//再通过指明 向GL_ARRAY_BUFFER上绑定的缓冲区(即vertex_buffer[0])进行初始化,来完成vertex_buffer[0]的初始化
//注意这里是|而不是||
目前看来glNamedBufferStorage还是方便一些。
3 更新缓冲中的内容 清单5.2-5.3
初始化完成后,我们要知道怎样对之前缓冲区中的初始数据进行修改,这里有两类方法一是直接拷贝覆盖,二是通过指针拷贝覆盖。而两种方法又各自分为直接对缓冲区对象操作、通过绑定点进行间接操作(就和glBufferStorage与glNamedBufferStorage的关系一样)
我将他们依次展示一遍:
直接拷贝(对绑定点间接操作)
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer[0]);
glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(Data0),Data0);
直接拷贝(对缓冲区对象直接操作)
glNamedBufferSubData(vertex_buffer[1],0,sizeof(Data1),Data1);
指针拷贝(对绑定点间接操作)
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer[0]);
void* vptr0 = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);//找到位置
memcpy(vptr0, Data0, sizeof(Data0));
glUnmapBuffer(GL_ARRAY_BUFFER);//解除对缓存的映射
指针拷贝(对缓冲区对象直接操作)
void* vptr1 = glMapNamedBuffer(vertex_buffer[1], GL_WRITE_ONLY);//找到位置
memcpy(vptr1, Data1, sizeof(Data1));
glUnmapNamedBuffer(vertex_buffer[1]