Nic*_*las 12 opengl opengl-es opengl-4
OpenGL的4.3和OpenGL ES 3.1添加若干替代功能用于指定顶点数组:glVertexAttribFormat,glBindVertexBuffers等,但我们已经有了用于指定顶点数组功能.即glVertexAttribPointer.
为什么要添加与旧版API相同的新API?
新API如何运作?
Nic*_*las 31
glVertexAttribPointer 有两个缺陷,其中一个是半主观的,另一个是客观的.
第一个缺陷是它的依赖性GL_ARRAY_BUFFER.这意味着行为glVertexAttribPointer取决于GL_ARRAY_BUFFER被调用时绑定的内容.但是一旦它被召唤,什么必然GL_ARRAY_BUFFER不再重要; 缓冲区对象的引用被复制到VAO中.所有这些都非常不直观和令人困惑,即使对一些半经验的用户也是如此.
它还要求您将缓冲区中的偏移量作为"指针"提供,而不是作为整数字节偏移量.这意味着您执行从整数到指针的笨拙演员(必须与驱动程序中同样笨拙的演员匹配).
第二个缺陷是它将逻辑上相互分离的两个操作混为一谈.为了定义OpenGL可以读取的顶点数组,您必须提供两件事:
glVertexAttribPointer同时提供这两者.的GL_ARRAY_BUFFER缓冲器对象,加上偏移"指针"和跨距定义数据被存储,以及如何获取它.其他参数描述了单个数据单元的外观.我们称之为数组的顶点格式.
实际上,与顶点格式相比,用户更有可能改变顶点数据的来源.毕竟,场景中的许多对象以相同的方式存储它们的顶点.无论那种方式如何:位置为3个浮点数,颜色为4个无符号字节,tex-coords为2个无符号短点等.一般来说,您只有几个顶点格式.
而您拥有更多从中提取数据的位置.即使对象都来自同一缓冲区,您也可能希望更新该缓冲区内的偏移量以从对象切换到对象.
使用glVertexAttribPointer,您无法仅更新偏移量.您必须一次指定整个格式+缓冲区信息.每次.
VAO减轻了必须为每个对象进行所有这些调用,但事实证明它们并没有真正解决问题.哦,当然,你不必实际打电话glVertexAttribPointer.但是,这并不能改变一个事实,改变顶点格式是昂贵的.
如此处所述,更改顶点格式非常昂贵.当您绑定新的VAO时(或者更确切地说,当您在绑定新的VAO之后进行渲染时),实现要么更改顶点格式,要么必须比较两个VAO以查看它们定义的顶点格式是否不同.无论哪种方式,它都在做它不需要做的工作.
glVertexAttribFormat并glBindVertexBuffer解决这两个问题.glBindVertexBuffer直接指定缓冲区对象,并将字节偏移量作为实际(64位)整数.所以没有笨拙的使用GL_ARRAY_BUFFER绑定; 该绑定仅用于操作缓冲区对象.
并且因为两个独立的概念现在是单独的函数,您可以拥有一个存储格式的VAO,绑定它,然后为您渲染的每个对象或一组对象绑定顶点缓冲区.更改顶点缓冲区绑定状态比顶点格式状态便宜.
单独的属性绑定功能就像这样工作.glVertexAttribPointer函数提供属性的所有顶点格式参数.它的每个参数与等效调用的参数具有完全相同的含义glVertexArrayAttribFormat.
事情变得有点混乱的地方glVertexAttrib*Format.
它的第一个参数是索引.但这不是属性位置; 它只是一个缓冲区绑定点.这是属性位置的单独数组,具有自己的最大限制.所以,你的缓冲区绑定到索引0处的事实意味着什么在哪里属性位置0从获取数据.
缓冲区绑定和属性位置之间的连接由glVertexAttrib*Pointer.定义.第一个参数是属性位置,第二个参数是用于获取该属性的位置的缓冲区绑定索引.由于函数的名称以"VertexAttrib"开头,因此您应该将其视为顶点格式状态的一部分,因此更改成本很高.
偏移的性质最初也可能有点令人困惑.glBindVertexBuffer有一个偏移参数.但同样如此glVertexAttribBinding.但是这些抵消意味着不同的东西.理解差异的最简单方法是使用交错数据结构的示例:
struct Vertex
{
GLfloat pos[3];
GLubyte color[4];
GLushort texCoord[2];
};
Run Code Online (Sandbox Code Playgroud)
顶点缓冲区绑定偏移量指定从缓冲区对象的起点到第一个顶点索引的字节偏移量.也就是说,当您渲染索引0时,GPU将从缓冲区对象的地址+绑定偏移量中获取内存.
顶点格式偏移指定从每个顶点的起点到该特定属性的数据的偏移量.如果缓冲区中的数据是由定义的glVertexAttribFormat,那么每个属性的偏移量将是:
glVertexAttribFormat(0, ..., offsetof(Vertex, pos)); //AKA: 0
glVertexAttribFormat(1, ..., offsetof(Vertex, color)); //Probably 12
glVertexAttribFormat(2, ..., offsetof(Vertex, texCoord)); //Probably 16
Run Code Online (Sandbox Code Playgroud)
这样的结合的偏移定义,其中顶点0是在存储器中,而格式偏移定义,其中每个属性的数据来自内的顶点.
最后要理解的是缓冲区绑定是定义步幅的位置.这可能看起来很奇怪,但从硬件角度考虑它.
缓冲区绑定应包含硬件将顶点索引或实例索引转换为内存位置所需的所有信息.完成后,顶点格式解释了如何解释该内存位置中的字节.
这也是实例除数是缓冲区绑定状态via的一部分的原因glBindVertexBuffer.硬件需要知道除数才能将实例索引转换为内存地址.
当然,这也意味着您不再需要依赖OpenGL来为您计算步幅.在上面的演员表中,你只需使用Vertex.
单独的属性格式完全覆盖了旧glVertexBindingDivisor模型,以至于旧函数现在完全根据新函数定义:
void glVertexAttrib*Pointer(GLuint index?, GLint size?, GLenum type?, {GLboolean normalized?,} GLsizei stride?, const GLvoid * pointer?)
{
glVertexAttrib*Format(index, size, type, {normalized,} 0);
glVertexAttribBinding(index, index);
GLuint buffer;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, buffer);
if(buffer == 0)
glErrorOut(GL_INVALID_OPERATION); //Give an error.
if(stride == 0)
stride = CalcStride(size, type);
GLintptr offset = reinterpret_cast<GLintptr>(pointer);
glBindVertexBuffer(index, buffer, offset, stride);
}
Run Code Online (Sandbox Code Playgroud)
请注意,此等效函数对属性位置和缓冲区绑定索引使用相同的索引值.如果您正在进行交错属性,则应尽可能避免这种情况; 相反,对从同一缓冲区交错的所有属性使用单个缓冲区绑定.
| 归档时间: |
|
| 查看次数: |
2480 次 |
| 最近记录: |