OpenGL 如何填充缓冲区并将它们读回?
How does OpenGL fill buffers and read them back?
我使用带有一堆 GLfloats 的 OpenGL 缓冲区作为顶点缓冲区,一切都很好。 GLfloats 的格式为 [x1, y1, z1, x2, y2, z2, ...]
.
但是,在遵循 this tutorial 时,它告诉我改用 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
填充缓冲区,以及(如果是,如何)读回它?
glm::vec3 只是结构中的三个浮点数。因此,将 glm::vec3 的地址传递给 gl 函数实际上与将地址传递给 float 数组的第一个元素做同样的事情。 GLfloat 只是 float btw 的 typedef。
同样的原则也适用于从 gl 中读取数据。内存中 glm::vec3 的 x 个元素的数组等效于具有 3x 个元素的 GLfloat(float)数组。
根据OpenGL's documentation,glBufferData()
需要一个指向data
的指针(即数组,即顶点的坐标) .
我们先来看看glm::vec3
的实现。
如果你签出 glm's Github repo, you'll see that, depending on your compilation flags, glm::vec3
is a typedef
of highp_vec3
which is a typedef
of tvec3<float, highp>
。
tvec3
在 type_vec3.hpp (included by vec3.hpp) and the class (template) methods are defined in type_vec3.inl.
中声明
特别是,operator[]
's definition 是:
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
是 "array" 的第一个元素,其中包含 glm::vec3
的坐标。然而,当我们回到type_vec3.h,我们发现:
union { T x, r, s; };
union { T y, g, t; };
union { T z, b, p; };
因此 x
、y
和 z
是 独立的属性 。但是多亏了 how class/struct members are laid out,它们可以被视为一个 从 &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()
.
我使用带有一堆 GLfloats 的 OpenGL 缓冲区作为顶点缓冲区,一切都很好。 GLfloats 的格式为 [x1, y1, z1, x2, y2, z2, ...]
.
但是,在遵循 this tutorial 时,它告诉我改用 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
填充缓冲区,以及(如果是,如何)读回它?
glm::vec3 只是结构中的三个浮点数。因此,将 glm::vec3 的地址传递给 gl 函数实际上与将地址传递给 float 数组的第一个元素做同样的事情。 GLfloat 只是 float btw 的 typedef。
同样的原则也适用于从 gl 中读取数据。内存中 glm::vec3 的 x 个元素的数组等效于具有 3x 个元素的 GLfloat(float)数组。
根据OpenGL's documentation,glBufferData()
需要一个指向data
的指针(即数组,即顶点的坐标) .
我们先来看看glm::vec3
的实现。
如果你签出 glm's Github repo, you'll see that, depending on your compilation flags, glm::vec3
is a typedef
of highp_vec3
which is a typedef
of tvec3<float, highp>
。
tvec3
在 type_vec3.hpp (included by vec3.hpp) and the class (template) methods are defined in type_vec3.inl.
特别是,operator[]
's definition 是:
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
是 "array" 的第一个元素,其中包含 glm::vec3
的坐标。然而,当我们回到type_vec3.h,我们发现:
union { T x, r, s; };
union { T y, g, t; };
union { T z, b, p; };
因此 x
、y
和 z
是 独立的属性 。但是多亏了 how class/struct members are laid out,它们可以被视为一个 从 &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()
.