glBufferData 和 glBufferSubData 偏移量

glBufferData and glBufferSubData Offset

我正在尝试在 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)

由于以下回复,还修复了一些其他错误!

没有看到你的着色器,我不能 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 上最终取决于您。如果您故意按照自己的方式存储数据,请在评论中告诉我,我会更新答案。

好的,现在是着色器位。

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

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 了解更多信息。