学习正确使用VBOs

5
所以我一直在尝试自学使用VBO,以提高我的OpenGL项目的性能并学习比固定功能渲染更高级的东西。但是我没有找到很好的教程;到目前为止我找到的最好的教程是Songho's tutorialsOpenGL.org上的内容,但似乎我缺少一些背景知识才能完全理解正在发生的事情,虽然我无法确定我没有理解的确切内容,除了一些参数的用法。
在任何情况下,我继续前进,并编写了一些被拼凑起来的代码,至少不会崩溃,但会导致奇怪的结果。我想要渲染的是这个(使用固定功能渲染;它应该是棕色的,背景是灰色的,但我的所有OpenGL截图似乎都采用洋红色作为它们最喜欢的颜色;也许是因为我使用SFML作为窗口?)。

Target image

但我得到的是这个:

Actual image

我很困惑。这是我使用的相关代码,首先是为了设置缓冲区对象(我分配了大量内存,根据这个人的建议分配4-8MB):

GLuint WorldBuffer;
GLuint IndexBuffer;

...

glGenBuffers(1, &WorldBuffer);
glBindBuffer(GL_ARRAY_BUFFER, WorldBuffer);
int SizeInBytes = 1024 * 2048;
glBufferData(GL_ARRAY_BUFFER, SizeInBytes, NULL, GL_STATIC_DRAW);

glGenBuffers(1, &IndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer);
SizeInBytes = 1024 * 2048;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, SizeInBytes, NULL, GL_STATIC_DRAW);

然后将数据上传到缓冲区。请注意,CreateVertexArray()会在传递的位置上填充向量与顶点数据,每个顶点都贡献了3个浮点数用于位置和3个浮点数用于法线(各种教程中最令人困惑的事情之一是我应该以什么格式存储和传输我的实际顶点数据; 这似乎是一个不错的近似):

std::vector<float>* VertArray = new std::vector<float>;
pWorld->CreateVertexArray(VertArray);
unsigned short Indice = 0;
for (int i = 0; i < VertArray->size(); ++i)
{
    std::cout << (*VertArray)[i] << std::endl;
    glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(float), sizeof(float), &((*VertArray)[i]));
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, i * sizeof(unsigned short), sizeof(unsigned short), &(Indice));
    ++Indice;
}
delete VertArray;
Indice -= 1;

之后,在游戏循环中,我使用了这段代码:

    glBindBuffer(GL_ARRAY_BUFFER, WorldBuffer);        
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer);    

    glEnableClientState(GL_VERTEX_ARRAY);         
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glNormalPointer(GL_FLOAT, 0, 0);

    glDrawElements(GL_TRIANGLES, Indice, GL_UNSIGNED_SHORT, 0);

    glDisableClientState(GL_VERTEX_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

我坦率地说 - 我不确定glVertexPointer()glNormalPointer()的第三个参数应该是什么(步长是字节偏移量,但Songho在值之间使用了0字节的偏移量 - 什么?),或者这两者的最后一个参数是什么。初始值被称为0;但它应该是一个指针。传递空指针以获取数组的第一个坐标/法线值似乎很奇怪。This guy使用BUFFER_OFFSET(0)BUFFER_OFFSET(12),但当我尝试时,我被告知BUFFER_OFFSET()未定义。

此外,glDrawElements()的最后一个参数应该是一个地址,但同样,Songho使用地址0。如果我使用&IndexBuffer而不是0,则什么都没有渲染,只有背景。

有人能为我解惑,或者至少指点我一些东西帮助我弄清楚吗?谢谢!

3个回答

7
最初的值被称为0;但它应该是一个指针。上下文(不是OpenGL的意思)很重要。如果调用其中一个gl * Pointer函数而没有将缓冲对象绑定到GL_ARRAY_BUFFER,则它是指向客户端进程地址空间的指针。如果将缓冲对象绑定到GL_ARRAY_BUFFER,则它是指向当前绑定缓冲对象的偏移量(您可以认为BO形成了一个虚拟地址空间,gl * Pointer的参数就是指向该服务器端地址空间的指针)。现在让我们看一下您的代码。
std::vector<float>* VertArray = new std::vector<float>;

您不应该混合使用STL容器和new,了解RAII模式。

(注:RAII指资源获取即初始化,是一种C++编程技巧)
pWorld->CreateVertexArray(VertArray);

这是个问题,因为你稍后会删除VertexArray,留下一个悬空指针。不好。

unsigned short Indice = 0;
for (int i = 0; i < VertArray->size(); ++i)
{
    std::cout << (*VertArray)[i] << std::endl;
    glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(float), sizeof(float), &((*VertArray)[i]));

您应该使用glBufferSubData提交大批量数据,而不是单个数据点。
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, i * sizeof(unsigned short), sizeof(unsigned short), &(Indice));

你现在只是将增量索引传递到GL_ELEMENT_ARRAY_BUFFER中,从而枚举顶点。 为什么?你可以使用glDrawArrays而不是glDrawElements来完成这个工作,省去额外的步骤。
    ++Indice;
}
delete VertArray;

您正在删除VertArray,因此保留了一个悬空指针。
Indice -= 1;

为什么您不直接使用循环计数器i

那么该如何修复呢?像这样:

std::vector<float> VertexArray;
pWorld->LoadVertexArray(VertexArray); // World::LoadVertexArray(std::vector<float> &);

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*VertexArray->size(), &VertexArray[0] );

当然,如果您没有枚举顶点,而是有一个面→顶点索引列表,则必须使用glDrawElements。

并使用glDrawArrays;

谢谢您的批评和解释!我会修订我的代码,希望这次能够更成功地理解它。 - GarrickW

4
不要为每个顶点调用glBufferSubData。这与VBO的用途不符。应该创建一个包含所有顶点数据的大缓冲区,然后一次性将其传递给OpenGL。
请参阅http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml 使用VBO时,这些指针是相对于VBO数据的。因此,它通常为0或小偏移值。 stride = 0 表示数据紧密打包,OpenGL可以从其他参数计算出stride
我通常像这样使用VBO:
struct Vertex
{
    vec3f position;
    vec3f normal;
};

Vertex[size] data;

...

glBufferData(GL_ARRAY_BUFFER, size*sizeof(Vertex), data, GL_STATIC_DRAW);

...

glVertexPointer(3,GL_FLOAT,sizeof(Vertex),offsetof(Vertex,position));
glNormalPointer(3,GL_FLOAT,sizeof(Vertex),offsetof(Vertex,normal));

只需传递单个顶点数据块。然后使用gl*Pointer描述数据如何使用offsetof宏进行打包。


所以stride=0意味着我已经将每个坐标紧密地打包在同一个数组中,然后,如果我理解正确的话。我能否像使用Vertex[]一样方便地使用std::vector<Vertex>? - GarrickW
@GarrickW 你应该可以的,是的:std::vector< T > a; ..... glBufferData( GL_ARRAY_BUFFER, sizeof( T ) * a.size(), &a[0], GL_STATIC_DRAW ); - zeboidlund

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接