学习正确使用VBO

时间:2011-10-19 09:22:17

标签: c++ opengl codeblocks vbo sfml

所以我一直在努力教自己使用VBO,以提高我的OpenGL项目的性能,并学习比固定功能渲染更高级的东西。但是我没有找到一个体面的教程方式;到目前为止我发现的最好的是Songho's tutorialsOpenGL.org的东西,但我似乎缺少某种背景知识来完全理解正在发生的事情,尽管我无法准确地说出来它是什么我没有得到,保存一些参数的使用。

在任何情况下,我都在前进并提出一些相似的代码,至少不会崩溃,但它会导致奇怪的结果。我要渲染的是这个(使用固定功能渲染;它应该是棕色的,背景为灰色,但我所有的OpenGL截图似乎都采用洋红色作为他们喜欢的颜色;也许是因为我在窗口使用SFML?)。

Target image

但我得到的是:

Actual image

我很茫然。这是我使用的相关代码,首先用于设置缓冲区对象(我根据this guy's建议分配大量内存以分配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()的第三个参数应该是什么(stride是以字节为单位的偏移,但是Songho在值之间使用0字节的偏移 - 什么?),或者其中任何一个的最后一个参数是什么。初始值称为0;但它应该是一个指针。传递空指针以获得数组的第一个坐标/正常值似乎很奇怪。 This guy使用BUFFER_OFFSET(0)BUFFER_OFFSET(12),但是当我尝试使用时,我被告知BUFFER_OFFSET()未定义。

另外,glDrawElements()的最后一个参数应该是一个地址,但是,Songho再次使用地址0.如果我使用&IndexBuffer而不是0,我得到一个没有任何东西的空白屏幕完全渲染,除了背景。

有人可以启发我,或者至少指出一些可以帮我解决这个问题的方向吗?谢谢!

3 个答案:

答案 0 :(得分:7)

  

初始值为0;但它应该是一个指针。

上下文(不代表OpenGL)很重要。如果调用gl * Pointer函数之一而没有将Buffer Object绑定到GL_ARRAY_BUFFER,则它是指向客户端进程地址空间的指针。如果缓冲区对象绑定到GL_ARRAY_BUFFER,则它是当前绑定缓冲区对象的偏移量(您可能会形成一个虚拟地址空间,gl *指针的参数就是指向该服务器端地址空间的指针)。 / p>

现在让我们来看看您的代码

std::vector<float>* VertArray = new std::vector<float>;

你不应该真正混合STL容器和new,了解RAII模式。

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,从而枚举顶点。为什么?你可以使用glDrawElements的glDrawArrays,而无需额外的工作。

    ++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] );

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

答案 1 :(得分: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宏打包数据。

答案 2 :(得分:0)