glBufferData 和 glBufferSubData 偏移量

Posted

技术标签:

【中文标题】glBufferData 和 glBufferSubData 偏移量【英文标题】:glBufferData and glBufferSubData Offset 【发布时间】:2016-04-21 22:08:36 【问题描述】:

我正在尝试在 OpenGL 3.3 中渲染 Suzanne(来自 Blender),但缓冲区数据似乎不正确。我明白了:

https://gyazo.com/ab82f9acb6854a49fccc527ed96cc4e8

我也尝试用简单的纹理渲染一个球体:

https://gyazo.com/85c1e87fcc4eab128ca37b1a0cb1deaa

我的导入器将顶点数据作为单个浮点数插入到 std::vector 中:

if(line.substr(0,2) == "v ")

/** Vertex position */
    std::istringstream s(line.substr(2));
    float v[3];
    s >> v[0]; s >> v[1]; s >> v[2];

    this->vertices.push_back(v[0]);
    this->vertices.push_back(v[1]);
    this->vertices.push_back(v[2]);

我设置数组缓冲区如下:

glGenBuffers(1, &this->vbo);
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);

glBufferData(GL_ARRAY_BUFFER,
    sizeof(float)*(this->vertices.size()+this->textures.size()+this->normals.size()),
    NULL,
    GL_STATIC_DRAW);

然后我使用 glBufferSubData 插入实际数据

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*this->vertices.size(), this->vertices.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*this->vertices.size(), sizeof(float)*this->textures.size(), this->textures.data());
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float)*(this->vertices.size()+this->textures.size()), sizeof(float)*this->normals.size(), this->normals.data());

我也以同样的方式插入索引(当然是 GL_ELEMENT_ARRAY_BUFFER)。

然后我指向信息:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)(sizeof(float)*this->v.size()));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (GLvoid*)(sizeof(float)*this->v.size()+this->vt.size()));

我的顶点着色器接收如下数据:

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in vec3 normals;

我搞砸了偏移量吗?

编辑: 我发现了最大的问题!我编写了一个外部 Lua 程序来将 obj 文件转换为更容易导入的格式,但它最终弄乱了数据并复制了 "f #/#/# #/#/# #/#/ 上的索引#" 所以文件看起来像这样 (x->y->x) 而不是 (x->y->z)

感谢下面的回复,还修复了一些其他错误!

【问题讨论】:

好收获!很高兴你能够让它工作。 非常感谢您的帮助!现在一切正常。 :D 我意识到当我绘制网格的“线框”时,我使用的是 GL_LINE_LOOP 而不是 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);因此,网格内部的额外面。这是证明:P gyazo.com/928640e745bfe3041798954f3f32079a 【参考方案1】:

如果没有看到您的着色器,我无法 100% 确定您对 glVertexAttribPointer 的调用是否合法。我也不知道你是否想要在单个 VBO 中交错的顶点数据。您当前拥有的内容首先包含所有顶点位置,然后是所有纹理坐标,最后是所有法线。

要交错数据,您首先需要将所有数据放入单个数组(或向量)中,以便每个顶点重复PPPTTNNN 模式。其中PPP 是三个位置浮点数,TT 是两个 texcoord 浮点数,NNN 是三个普通浮点数。

它看起来像这样(使用虚假的类型、值和间距来帮助说明模式):

float[] vertices = 
 /* pX, pY, pZ, tX, tY, nX, nY, nZ */
     1,  1,  1,  0,  0,  1,  1,  1,     // vertex 1
     0,  0,  0,  1,  1,  0,  0,  0,     // vertex 2
     1,  1,  1,  0,  0,  1,  1,  1,     // vertex 3
    ...   
;

假设您将所有内容放入名为vertices 的单个向量中,然后您可以使用单个命令将其上传:

glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->vertices.size(), this->vertices.data(), GL_STATIC_DRAW);

您还可以将每个属性放入其自己的 VBO。您决定如何在 GPU 上存储数据最终取决于您。如果您故意以您拥有的方式存储数据,请在 cmets 中告诉我,我会更新答案。

好的,现在是着色器位。

假设您有一个如下所示的顶点着色器:

in vec3 position;
in vec2 texcoord;
in vec3 normal;
out vec2 uv;

void main() 
    gl_Position = vec4(position, 1);
    uv = texcoord;

还有一个像这样的片段着色器:

in vec2 uv;
uniform sampler2D image;
out vec4 color;

void main() 
    color = texture(image, uv);

那么您将需要以下glVertexAttribPointer 调用:

int stride = (3 + 2 + 3) * sizeof(float);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)3);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)5);

注意每个调用的第一个参数是不同的数字。这分别对应于顶点着色器中的positiontexcoordnormal

另外,纹理坐标通常只有一对浮点数(例如vec2 texcoord),所以我将第二个参数更改为2 用于 texcoord 调用。

最后一个参数,当使用交错数组时,只需要指定每个顶点的偏移量。因此我们得到035分别表示位置偏移、纹理坐标偏移和法线偏移。

希望这能让你到达你想去的地方。

查看docs.gl page on glVertexAttribPointer 了解更多信息。

【讨论】:

感谢您的回复!我会用着色器更新我的问题!我从 obj 文件中获取数据的方式使得仅追加数据而不是交错数据变得更加容易。 @exide 你将如何为所有顶点位置设置 iBO,然后是所有纹理坐标,最后是所有法线情况? @feisal 我建议专门为此创建一个新问题。

以上是关于glBufferData 和 glBufferSubData 偏移量的主要内容,如果未能解决你的问题,请参考以下文章

大小太大时 VBO + glBufferData 崩溃

何时在 OpenGL 中使用 glBufferData

glBufferData 不断失败并出现 GL_INVALID_ENUM 即使它不应该

关于webgl中glBufferData的第二个参数的问题

缓冲区和 glVertexAttribPointer 之间的关系

OpenGL 2.1:glMapBuffer 和使用提示