glVertexAttribPointer和glVertexAttribFormat:有什么区别? [英] glVertexAttribPointer and glVertexAttribFormat: What's the difference?

查看:158
本文介绍了glVertexAttribPointer和glVertexAttribFormat:有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

OpenGL 4.3和OpenGL ES 3.1添加了一些用于指定顶点数组的替代函数:glVertexAttribFormatglBindVertexBuffers等.但是我们已经具有用于指定顶点数组的函数.即glVertexAttribPointer.

OpenGL 4.3 and OpenGL ES 3.1 added several alternative functions for specifying vertex arrays: glVertexAttribFormat, glBindVertexBuffers, etc. But we already had functions for specifying vertex arrays. Namely glVertexAttribPointer.

  1. 为什么要添加与旧API具有相同功能的新API?

  1. Why add new APIs that do the same thing as the old ones?

新API如何工作?

推荐答案

glVertexAttribPointer有两个缺陷,一个是半主观的,另一个是目标.

glVertexAttribPointer has two flaws, one of them semi-subjective, the other objective.

第一个缺陷是它对GL_ARRAY_BUFFER的依赖性.这意味着glVertexAttribPointer的行为取决于调用它时绑定到GL_ARRAY_BUFFER的任何内容.但是一旦被调用,绑定到GL_ARRAY_BUFFER的内容就不再重要了;缓冲区对象的引用将复制到VAO中.即使对于某些半经验的用户,所有这些都是非常直观且令人困惑的.

The first flaw is its dependency on GL_ARRAY_BUFFER. This means that the behavior of glVertexAttribPointer is contingent on whatever was bound to GL_ARRAY_BUFFER at the time it was called. But once it is called, what is bound to GL_ARRAY_BUFFER no longer matters; the buffer object's reference is copied into the VAO. All this is very unintuitive and confusing, even to some semi-experienced users.

还要求您在缓冲区对象中提供一个偏移量作为指针",而不是整数字节偏移量.这意味着您要执行从整数到指针的尴尬转换(必须与驱动程序中同样尴尬的转换匹配).

It also requires you to provide an offset into the buffer object as a "pointer", rather than as an integer byte offset. This means that you perform an awkward cast from an integer to a pointer (which must be matched by an equally awkward cast in the driver).

第二个缺陷是它将两个逻辑上完全分开的操作合并在一起.为了定义OpenGL可以读取的顶点数组,您必须提供两件事:

The second flaw is that it conflates two operations that, logically, are quite separate. In order to define a vertex array that OpenGL can read, you must provide two things:

  • 如何从内存中获取数据.
  • 数据看起来像什么.

glVertexAttribPointer同时提供这两种功能. GL_ARRAY_BUFFER缓冲区对象,加上偏移量指针"和跨距定义了数据的存储位置以及如何获取数据.其他参数描述单个数据单元的外观.让我们称之为数组的顶点格式.

glVertexAttribPointer provides both of these simultaneously. The GL_ARRAY_BUFFER buffer object, plus the offset "pointer" and stride define where the data is stored and how to fetch it. The other parameters describes what a single unit of data looks like. Let us call this the vertex format of the array.

实际上,与顶点格式相比,用户更有可能更改顶点数据的来源.毕竟,场景中的许多对象都以相同的方式存储其顶点.无论采用哪种方式:3个浮点表示位置,4个无符号字节表示颜色,2个无符号短裤表示tex-coords等,通常来说,您只有几种顶点格式.

As a practical matter, users are far more likely to change where vertex data comes from than vertex formats. After all, many objects in the scene store their vertices in the same way. Whatever that way may be: 3 floats for position, 4 unsigned bytes for colors, 2 unsigned shorts for tex-coords, etc. Generally speaking, you have only a few vertex formats.

您在更多的位置提取数据.即使对象全部来自同一个缓冲区,您也可能希望更新该缓冲区内的偏移量 ,以从一个对象切换到另一个对象.

Whereas you have far more locations where you pull data from. Even if the objects all come from the same buffer, you will likely want to update the offset within that buffer to switch from object to object.

使用glVertexAttribPointer,您不能仅更新偏移量.您必须一次指定整个格式+缓冲区信息.每次.

With glVertexAttribPointer, you can't update just the offset. You have to specify the whole format+buffer information all at once. Every time.

VAO减轻了必须针对每个对象进行所有这些调用的麻烦,但事实证明,它们并不能真正解决问题.哦,可以,您不必实际调用glVertexAttribPointer.但这并不能改变更改顶点格式昂贵的事实.

VAOs mitigate having to make all those calls per object, but it turns out that they don't really solve the problem. Oh sure, you don't have to actually call glVertexAttribPointer. But that doesn't change the fact that changing vertex formats is expensive.

如此处所述,更改顶点格式非常昂贵.当您绑定一个新的VAO时(或者,当您在绑定一个新的VAO之后进行渲染),实现会更改顶点格式,而无论是否必须比较两个VAO来查看它们定义的顶点格式是否不同.无论哪种方式,它都在做不需要做的工作.

As discussed here, changing vertex formats is pretty expensive. When you bind a new VAO (or rather, when you render after binding a new VAO), the implementation either changes the vertex format regardless or has to compare the two VAOs to see if the vertex formats they define are different. Either way, it's doing work that it doesn't need to be doing.

glVertexAttribFormatglBindVertexBuffer修复了这两个问题. glBindVertexBuffer直接指定缓冲区对象,并将字节偏移量作为实际(64位)整数.因此,没有笨拙地使用GL_ARRAY_BUFFER绑定;该绑定仅用于操作缓冲区对象.

glVertexAttribFormat and glBindVertexBuffer fix both of these problems. glBindVertexBuffer directly specifies the buffer object and takes the byte offset as an actual (64-bit) integer. So there's no awkward use of the GL_ARRAY_BUFFER binding; that binding is solely used for manipulating the buffer object.

并且由于这两个独立的概念现在是独立的功能,因此您可以拥有一个VAO,用于存储格式,对其进行绑定,然后为使用其渲染的每个对象或对象组绑定顶点缓冲区.更改顶点缓冲区绑定状态要比顶点格式状态便宜.

And because the two separate concepts are now separate functions, you can have a VAO that stores a format, bind it, then bind vertex buffers for each object or group of objects that you render with. Changing vertex buffer binding state is cheaper than vertex format state.

请注意,这种分离在GL 4.5的直接状态访问API 中已正式化.也就是说,没有glVertexAttribPointer的DSA版本.您必须使用glVertexArrayAttribFormat和其他单独的格式API.

Note that this separation is formalized in GL 4.5's direct state access APIs. That is, there is no DSA version of glVertexAttribPointer; you must use glVertexArrayAttribFormat and the other separate format APIs.

单独的属性绑定功能按以下方式工作. glVertexAttrib*Format函数提供属性的所有顶点格式设置参数.它的每个参数与对glVertexAttrib*Pointer的等效调用中的参数具有完全相同的含义.

The separate attribute binding functions work like this. glVertexAttrib*Format functions provides all of the vertex formatting parameters for an attribute. Each of its parameters have the exact same meaning as the parameters from the equivalent call to glVertexAttrib*Pointer.

glBindVertexBuffer使事情有些混乱.

它的第一个参数是索引.但这不是属性的位置.它只是一个缓冲区绑定点.这是与属性位置分开的数组,具有其自己的最大限制.因此,将缓冲区绑定到索引0的事实意味着关于属性位置0从何处获取其数据的任何信息.

Its first parameter is an index. But this is not an attribute location; it is merely a buffer binding point. This is a separate array from attribute locations with its own maximum limit. So the fact that you bind a buffer to index 0 means nothing about where attribute location 0 gets its data from.

缓冲区绑定和属性位置之间的连接由glVertexAttribBinding定义.第一个参数是属性位置,第二个参数是用于获取该属性位置的缓冲区绑定索引.由于该函数的名称以"VertexAttrib"开头,因此您应将其视为 vertex format 状态的一部分,因此更改成本很高.

The connection between buffer bindings and attribute locations is defined by glVertexAttribBinding. The first parameter is the attribute location, and the second is the buffer binding index to fetch that attribute's location with. Since the function's name starts with "VertexAttrib", you should consider this to be part of the vertex format state and thus is expensive to change.

起初,偏移量的性质可能也会有些混乱. glVertexAttribFormat具有偏移参数.但是glBindVertexBuffer也是如此.但是这些偏移量意味着不同的事情.理解差异的最简单方法是使用交错数据结构的示例:

The nature of offsets may be a bit confusing at first as well. glVertexAttribFormat has an offset parameter. But so too does glBindVertexBuffer. But these offsets mean different things. The easiest way to understand the difference is by using an example of an interleaved data structure:

struct Vertex
{
    GLfloat pos[3];
    GLubyte color[4];
    GLushort texCoord[2];
};

顶点缓冲区绑定偏移量指定从缓冲区对象的开始到第一个顶点索引的字节偏移量.也就是说,当您渲染索引0时,GPU将从缓冲区对象的地址+绑定偏移量中获取内存.

The vertex buffer binding offset specifies the byte offset from the start of the buffer object to the first vertex index. That is, when you render index 0, the GPU will fetch memory from the buffer object's address + the binding offset.

顶点 format 偏移量指定从每个 vertex 的起始处到该特定属性数据的偏移量.如果缓冲区中的数据由Vertex定义,则每个属性的偏移量将为:

The vertex format offset specifies the offset from the start of each vertex to that particular attribute's data. If the data in the buffer is defined by Vertex, then the offset for each attribute would be:

glVertexAttribFormat(0, ..., offsetof(Vertex, pos)); //AKA: 0
glVertexAttribFormat(1, ..., offsetof(Vertex, color)); //Probably 12
glVertexAttribFormat(2, ..., offsetof(Vertex, texCoord)); //Probably 16

因此,绑定偏移量定义了内存中顶点0的位置,而格式偏移量则定义了每个属性的数据来​​自顶点内 的位置.

So the binding offset defined where vertex 0 is in memory, while the format offsets define where the each attribute's data comes from within a vertex.

最后要了解的是,缓冲区绑定是定义 stride 的位置.这看似奇怪,但请从硬件角度考虑.

The last thing to understand is that the buffer binding is where the stride is defined. This may seem odd, but think about it from the hardware perspective.

缓冲区绑定应包含硬件将顶点索引或实例索引转换为内存位置所需的所有信息.完成后,顶点格式将说明如何解释该内存位置中的字节.

The buffer binding should contain all of the information needed by the hardware to turn a vertex index or instance index into a memory location. Once that's done, the vertex format explains how to interpret the bytes in that memory location.

这也是为什么实例除数通过glVertexBindingDivisor属于缓冲区绑定状态的原因.硬件需要知道除数,才能将实例索引转换为内存地址.

This is also why the instance divisor is part of the buffer binding state, via glVertexBindingDivisor. The hardware needs to know the divisor in order to convert an instance index into a memory address.

当然,这也意味着您不再可以依靠OpenGL为您计算步幅.在上面的演员表中,您只需使用sizeof(Vertex).

Of course, this also means that you can no longer rely on OpenGL to compute the stride for you. In the above cast, you simply use sizeof(Vertex).

单独的属性格式完全覆盖了旧的glVertexAttribPointer模型,以至于现在可以完全根据新的定义旧功能:

Separate attribute formats completely covers the old glVertexAttribPointer model so well that the old function is now defined entirely in terms of the new:

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);
}

请注意,此等效函数对属性位置和缓冲区绑定索引使用相同的索引值.如果您要使用交错属性,则应尽可能避免这种情况.相反,请对从同一缓冲区插入的所有属性使用单个缓冲区绑定.

Note that this equivalent function uses the same index value for the attribute location and the buffer binding index. If you're doing interleaved attributes, you should avoid this where possible; instead, use a single buffer binding for all attributes that are interleaved from the same buffer.

这篇关于glVertexAttribPointer和glVertexAttribFormat:有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆