顶点数据应该以每个pertex或每个属性为基础进行布局吗
Should vertex data be laid out on per-pertex or per-attribute basis?
我有一段渲染网格的OpenGL代码。我使用VBO来渲染它们。现在,网格由具有以下属性的顶点组成:
glm::vec3 position;
glm::vec2 uv;
glm::vec4 color;
glm::vec3 normal;
glm::vec3 tangent;
glm::vec3 binormal;
目前,我在逐顶点的基础上渲染顶点,如下所示:
// Upload a vector of vertices
glBindBuffer(GL_ARRAY_BUFFER, &m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex), &m_vertices[0], GL_STATIC_DRAW);
// Set the "layout" of the vertex attributes
// Binormal
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) (sizeof(glm::vec3) * 3 + sizeof(glm::vec2) + sizeof(glm::vec4)));
// Tangent
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) (sizeof(glm::vec3) * 2 + sizeof(glm::vec2) + sizeof(glm::vec4)));
// Normal
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) (sizeof(glm::vec3) + sizeof(glm::vec2) + sizeof(glm::vec4)));
// Color
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) (sizeof(glm::vec3) + sizeof(glm::vec2)));
// UV
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) (sizeof(glm::vec3)));
// Position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*) 0);
// Draw
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_SHORT, 0);
现在,我看到人们的做法有点不同。有些先上传所有顶点位置,然后是UV数据,然后是法线等等。要对数据布局进行粗略可视化:
// P = position, U = uv, N = normal
// Per-vertex layout
PUNPUNPUNPUNPUNPUNPUNPUNPUNPUNPUNPUN
// Per-attribute layout
PPPPPPPPPPPPUUUUUUUUUUUUNNNNNNNNNNNN
这两种布局有什么区别吗?其中一个或另一个是否会导致任何性能问题,尤其是在数据不断更新的情况下?
您描述的第一个布局通常被称为"交错",通常被认为是有利的。其理由是,它会产生更多的本地内存访问模式,这些模式对缓存更友好。
使用不同布局的一个很好的理由是,如果某些属性的更新频率远高于其他属性。在极端情况下,其中一些是静态的,而另一些是频繁更新的,实际上,将静态属性保留在一个VBO中,使用GL_STATIC_DRAW
,并为频繁更改的属性使用GL_DYNAMIC_DRAW
使用的单独缓冲区可能是有益的。
@leemes在上面的一条评论中提出了另一个有趣的例子:如果您经常只使用属性的一个子集进行绘制调用,那么对它们进行不同的分组可能也是值得的。在这种情况下,您可以拥有总是在交错布局中使用的属性,并将很少使用的属性分开。
综上所述,渲染管道中通常会有更大的瓶颈,因此在目标合成基准之外可能很难测量差异。尽管如此,我认为保持一切尽可能精简是最值得的。尤其是现在大多数电脑/设备都是用电池供电的,你不想浪费任何东西。
相关文章:
- 我可以把基础班提升为儿童班吗
- 是否有内置方法可以强制转换为不同的基础类型,但保留常量限定符?
- 将作用域枚举转换为基础类型
- C ++基础私有方法在将自身转换为派生类后可以访问吗?
- 在编译时将强类型枚举器转换为其基础类型?
- 将整数值转换为以枚举为基础的字符串
- 将视觉C 媒体基础捕获应用程序转换为C 构建器
- 将指向类型化/大小的枚举的指针转换为指向基础类型的指针是否安全?
- 使用PYBIND11,如何为Array_t对象设置基础内存的所有权
- 为基础类构造函数提供初始化的成员
- 在CMAKE中,如何以每个用户为基础更改构建类型的默认编译器标志
- 顶点数据应该以每个pertex或每个属性为基础进行布局吗
- 如何将Boost数量数组类型双关为基础类型
- 为什么不能通过指针将 C++11 强类型枚举强制转换为基础类型?
- 有没有一种方法来运行Qt(c++)为基础的程序没有Qt安装
- 使一行运行一次,文本为基础的游戏
- 在QT c++中为包含布局的小部件添加滚动条
- 是否可以将枚举类转换为基础类型
- 给定一个数字 n 和两个整数 p1,p2 确定位置 p1 和 p2 中的位是否相同.位置 p1、p2 和 1 为基础
- 如果包含的对象也继承,我如何定义相互继承的容器?(以QObject为基础)