【问题标题】:How does OpenGL fill buffers and read them back?OpenGL 如何填充缓冲区并将它们读回?
【发布时间】:2016-01-08 14:55:36
【问题描述】:

我使用带有一堆 GLfloat 的 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 填充缓冲区,并且(如果是,如何)读回它?

【问题讨论】:

    标签: c++ opengl buffer glm-math


    【解决方案1】:

    根据OpenGL's documentationglBufferData()需要一个指向data的指针(即一个数组,即顶点的坐标)。

    我们先来看看glm::vec3的实现。

    如果您查看 glm's Github repo,您会发现,取决于您的编译标志glm::vec3typedef of highp_vec3,这是 typedef of tvec3&lt;float, highp&gt;

    tvec3type_vec3.hpp 中声明(包含在vec3.hpp 中),类(模板)方法在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 是包含glm::vec3 坐标的“数组”的第一个元素。然而,当我们回到type_vec3.h 时,我们发现:

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

    所以xyz独立的属性。但是感谢how class/struct members are laid out,它们可以被视为一个&amp;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&lt;glm::vec3&gt; vertices;,可以肯定地说&amp;vertices[0](在C++11 中为vertices.data())指向的数据的布局是:

    vertices == [vertice1 vertice2 ...]
             == [vertice1.x vertice1.y vertice1.z vertice2.x vertice2.y vertice2.z ...]
    

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

    【讨论】:

    • 太棒了!这很有帮助。作为奖励,您可以将“任何东西”传递给 OpenGL 缓冲区吗?说一个类的实例数组。我猜当你读回数据时,它不太工作,或者会吗?
    • 这取决于您 binding 的 GL 对象。但根据我的经验,如果你想上传顶点,你将不得不使用教程中提到的方法(即传递一个原始数组或 glm::vec3 等......基本上,x, y, z 的连续结构因此,如果您创建一个自定义类,该类的 only x, y, z 定义类似于 glm(或大小为 3 的数组),您应该可以传递一个包含其数量的向量实例。
    • @fordcars:嗯,GL 缓冲区对象只是具有指定大小的连续内存区域。所以你可以把你喜欢的东西放在那里,也可以读回来。但是,如果您打算将缓冲区用作 OpenGL 本身的输入,则需要根据您使用的缓冲区类型来计算一些限制。顶点属性可以是某些数据类型的 1 到 4 维向量,并且顶点数组需要以连续元素之间的恒定偏移量进行布局。将对象的实例放在那里可能会起作用,但您可能会浪费一些内存。
    【解决方案2】:

    glm::vec3 只是结构中的三个浮点数。因此,将 glm::vec3 的地址传递给 gl 函数实际上与将地址传递给浮点数组的第一个元素相同。 GLfloat 只是 float btw 的 typedef。

    同样的原则也适用于从 gl 读取数据。内存中glm::vec3的x个元素数组等价于3x个元素的GLfloat(float)数组。

    【讨论】:

      猜你喜欢
      • 2015-11-15
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      • 2017-01-05
      相关资源
      最近更新 更多