OpenGL如何填充缓冲区并读取它们

How does OpenGL fill buffers and read them back?

本文关键字:缓冲区 读取 填充 何填充 OpenGL      更新时间:2023-10-16

我使用了一个带有一堆glfloat的OpenGL缓冲作为顶点缓冲,一切都很好。glfloat的格式为[x1, y1, z1, x2, y2, z2, ...] .

但是,在遵循本教程时,它告诉我使用glm::vec3代替:

glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), &vertices[0], GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);

现在这段代码是有效的,我想知道OpenGL如何知道如何用glm::vec3填充缓冲区而不是GLfloats。然后我想知道,当我从缓冲区读取数据返回时,使用:

std::vector<glm::vec3> data;
glGetBufferSubData(mTarget, offset, vertexCount * sizeof(glm::vec3), &data[0]);`

这会产生一堆glm::vec3吗?所以问题是, OpenGL如何用glm::vec3填充缓冲区,并且(如果是这样,如何)它读回来?

根据OpenGL的文档,glBufferData()需要一个指向data的指针(即数组,即顶点的坐标)。

让我们先看一下glm::vec3的实现。

如果你查看glm的Github repo,你会看到,根据你的编译标志glm::vec3highp_vec3typedef, tvec3<float, highp>typedef

tvec3在type_vec3.hpp中声明(包含在vec3.hpp中),类(模板)方法在type_vec3.inl中定义。

具体来说,operator[]的定义是:

template <typename T, precision P>
GLM_FUNC_QUALIFIER T & tvec3<T, P>::operator[](typename tvec3<T, P>::length_type i)
{
    assert(i >= 0 && static_cast<detail::component_count_t>(i) < detail::component_count(*this));
    return (&x)[i];
}

给定这段代码,可以假设x是包含glm::vec3坐标的"数组"的第一个元素。但是,当返回到type_vec3.h时,发现:

union { T x, r, s; };
union { T y, g, t; };
union { T z, b, p; };

所以x, yz单独的属性。但是由于类/结构成员的布局方式,它们可以被视为从&x 开始的单个数组。

我们现在知道,glm::vec3(实际上是tvec3)以连续的方式存储坐标。但是它是否也存储其他属性呢?

好吧,我们可以继续深入研究代码,或者使用一个简单的程序来给出答案:

#include <iostream>
#include <ios>
#include <glm/vec3.hpp>
int main()
{
    const glm::vec3 v;
    const size_t sizeof_v   = sizeof(v);
    const size_t sizeof_xyz = sizeof(v.x) + sizeof(v.y) + sizeof(v.z);
    std::cout << "sizeof(v)  : " << sizeof_v   << std::endl;
    std::cout << "sizeof(xyz): " << sizeof_xyz << std::endl;
    std::cout << "sizeof(v) == sizeof(xyz) : " << std::boolalpha << (sizeof_v == sizeof_xyz) << std::endl;
}

在我的机器上打印:

sizeof(v)  : 12
sizeof(xyz): 12
sizeof(v) == sizeof(xyz) : true

因此,glm::vec3只存储 (x, y, z)的坐标。

现在,如果我们创建一个std::vector<glm::vec3> vertices;,可以肯定地说,&vertices[0](在c++ 11中是vertices.data())所指向的数据的布局是:
vertices == [vertice1 vertice2 ...]
         == [vertice1.x vertice1.y vertice1.z vertice2.x vertice2.y vertice2.z ...]

回到原来的问题——glBufferData()的要求:当你传递&vertices[0]时,你实际上是在传递data的地址(即指针),正如glBufferData()所期望的那样。同样的逻辑也适用于glGetBufferSubData()

glm::vec3只是一个结构体中的三个浮点数。因此,将glm::vec3的地址传递给gl函数的效果与将地址传递给float数组的第一个元素的效果相同。GLfloat只是float的一个类型定义。

同样的原理也适用于从gl中读取数据。内存中包含x个元素的glm::vec3数组相当于包含3x个元素的GLfloat (float)数组。