2011-10-19 45 views
5

所以我一直在试图教自己使用VBOs,以提高我的OpenGL项目的性能,并学习比固定函数渲染更先进的东西。但我没有找到一个体面的教程的方式很多;到目前为止我发现的最好的是Songho's tutorialsOpenGL.org的东西,但我似乎错过了某种背景知识以充分理解发生了什么,但我无法确切知道它是什么,我没有得到,保存几个参数的用法。在任何情况下,我都已经伪造了,并且提出了一些至少不会崩溃的破解代码,但这会导致奇怪的结果。我想渲染的是这个(使用固定功能渲染;它应该是棕色和背景灰色,但是我所有的OpenGL屏幕截图似乎都采用洋红色作为他们最喜欢的颜色;也许是因为我使用SFML作为窗口?)。学会正确使用VBOs

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()应该是第三个参数(步幅是偏移量是由tes,但Songho在值之间使用0字节的偏移量 - 什么?),或者这两者中的最后一个参数是什么。初始值被认为是0;但它应该是一个指针。传递一个空指针以获得数组的第一个坐标/标准值似乎很奇怪。 This guy使用BUFFER_OFFSET(0)BUFFER_OFFSET(12),但是当我尝试时,我被告知BUFFER_OFFSET()未定义。

另外,中glDrawElements()最后一个参数应该是一个地址,但同样,Songho如果我使用&IndexBuffer而不是0使用的0地址,我得到了一个空白屏幕无任何渲染的地方,除了背景。

有人能够启发我,或者至少指向我的方向,这将帮助我弄清楚这一点吗?谢谢!

回答

7

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

上下文(不是指OpenGL之一)很重要。如果其中一个gl *指针函数被调用时没有将缓冲区对象绑定到GL_ARRAY_BUFFER,那么它是一个指向客户进程地址空间的指针。如果缓冲区对象绑定到GL_ARRAY_BUFFER,它就是当前绑定的缓冲区对象的偏移量(您可能是BO形成一个虚拟地址空间,然后gl *指针的参数就是指向该服务器端地址空间的指针)。

现在,让我们看看你的代码

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,从而枚举顶点。为什么?你可以使用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]); 

并使用glDrawArrays;当然,如果你不枚举顶点,但有一个面→顶点索引列表,使用glDrawElements是强制性的。

+0

感谢您的批评和解释!我会修改我的代码,希望这次有更多的成功和理解。 – GarrickW

4

不要为每个顶点调用glBufferSubData。它错过了VBO的观点。您应该创建顶点数据的大缓冲区,然后一次传递给OpenGL。

阅读http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml

当使用维也纳组织这些指针是相对于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宏对数据进行打包。

+0

所以跨度= 0意味着我已经打包每个坐标并排在同一阵列中,那么,如果我理解正确。我可以像使用顶点[]一样简单地使用std :: vector 吗? – GarrickW

+0

@GarrickW你应该能够,是: '的std ::矢量< T >一个;'' .....'' glBufferData(GL_ARRAY_BUFFER,的sizeof(T)* a.size(),一个[0 ],GL_STATIC_DRAW); ' – zeboidlund