学习正确使用 VBO
因此,我一直在尝试自学使用 VBO,以提高 OpenGL 项目的性能并学习比固定功能渲染更高级的东西。但我还没有找到太多像样的教程;到目前为止,我发现的最好的教程是 Songho 的教程 以及 OpenGL.org,但我似乎缺少某种背景知识来完全理解正在发生的事情我无法确切地说出我没有得到什么,除了使用一些参数之外。
无论如何,我已经继续前进并提出了一些至少不会崩溃的代码,但它会导致奇怪的结果。我想要渲染的是这个(使用固定功能渲染;它应该是棕色和背景灰色,但我所有的OpenGL屏幕截图似乎都采用洋红色作为他们最喜欢的颜色;也许是因为我对窗口使用SFML?)。
不过,我得到的是:
我不知所措。这是我使用的相关代码,首先用于设置缓冲区对象(我根据 this 分配大量内存家伙的建议分配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;但它应该是一个指针。传递空指针以获得数组的第一个坐标/正常值似乎很奇怪。 这家伙使用BUFFER_OFFSET(0)
和BUFFER_OFFSET(12) )
,但是当我尝试这样做时,我被告知 BUFFER_OFFSET() 未定义。
另外,glDrawElements()
的最后一个参数应该是一个地址,但同样,Songho 使用地址 0。如果我使用 &IndexBuffer
而不是 0,我得到一个空白屏幕,除了背景之外根本没有任何渲染。
有人可以启发我,或者至少为我指明一些可以帮助我解决这个问题的方向吗?谢谢!
So I've been trying to teach myself to use VBOs, in order to boost the performance of my OpenGL project and learn more advanced stuff than fixed-function rendering. But I haven't found much in the way of a decent tutorial; the best ones I've found so far are Songho's tutorials and the stuff at OpenGL.org, but I seem to be missing some kind of background knowledge to fully understand what's going on, though I can't tell exactly what it is I'm not getting, save the usage of a few parameters.
In any case, I've forged on ahead and come up with some cannibalized code that, at least, doesn't crash, but it leads to bizarre results. What I want to render is this (rendered using fixed-function; it's supposed to be brown and the background grey, but all my OpenGL screenshots seem to adopt magenta as their favorite color; maybe it's because I use SFML for the window?).
What I get, though, is this:
I'm at a loss. Here's the relevant code I use, first for setting up the buffer objects (I allocate lots of memory as per this guy's recommendation to allocate 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);
Then for uploading the data into the buffer. Note that CreateVertexArray() fills the vector at the passed location with vertex data, with each vertex contributing 3 floats for position and 3 floats for normal (one of the most confusing things about the various tutorials was what format I should store and transfer my actual vertex data in; this seemed like a decent approximation):
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;
After that, in the game loop, I use this code:
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);
I'll be totally honest - I'm not sure I understand what the third parameter of glVertexPointer()
and glNormalPointer()
ought to be (stride is the offset in bytes, but Songho uses an offset of 0 bytes between values - what?), or what the last parameter of either of those is. The initial value is said to be 0; but it's supposed to be a pointer. Passing a null pointer in order to get the first coordinate/normal value of the array seems bizarre. This guy uses BUFFER_OFFSET(0)
and BUFFER_OFFSET(12)
, but when I try that, I'm told that BUFFER_OFFSET() is undefined.
Plus, the last parameter of glDrawElements()
is supposed to be an address, but again, Songho uses an address of 0. If I use &IndexBuffer
instead of 0, I get a blank screen without anything rendering at all, except the background.
Can someone enlighten me, or at least point me in the direction of something that will help me figure this out? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
上下文(不是指 OpenGL)很重要。如果调用 gl*Pointer 函数之一而没有将缓冲区对象绑定到 GL_ARRAY_BUFFER,则它是指向客户端进程地址空间的指针。如果缓冲区对象绑定到 GL_ARRAY_BUFFER,那么它是当前绑定缓冲区对象的偏移量(您可能认为 BO 形成虚拟地址空间,gl*Pointer 的参数就是指向该服务器端地址空间的指针)。
现在让我们看一下您的代码
您不应该真正混合 STL 容器和
new
,了解 RAII 模式。这是有问题的,因为稍后您将删除 VertexArray,留下一个悬空指针。不好。
您应该使用 glBufferSubData 提交大批量的数据,而不是单个数据点。
您仅将递增索引传递到 GL_ELEMENT_ARRAY_BUFFER 中,从而枚举顶点。为什么?您可以拥有此功能,而无需使用 glDrawArrays 代替 glDrawElements 进行额外的工作。
您正在删除 VertArray,从而保留一个悬空指针。
为什么不直接使用循环计数器
i
?那么如何解决这个问题呢?像这样:
并使用 glDrawArrays;当然,如果您不枚举顶点,但有一个面→顶点索引列表,则必须使用 glDrawElements。
The context (not meaning the OpenGL one) matters. If one of the gl*Pointer functions is called with no Buffer Object being bound to GL_ARRAY_BUFFER, then it is a pointer into client process address space. If a Buffer Object is bound to GL_ARRAY_BUFFER it's an offset into the currently bound buffer object (you may thing the BO forming a virtual address space, to which the parameter to gl*Pointer is then an pointer into that server side address space).
Now let's have a look at your code
You shouldn't really mix STL containers and
new
, learn about the RAII pattern.This is problematic, since you'll delete VertexArray later on, leaving you with a dangling pointer. Not good.
You should submit large batches of data with glBufferSubData, not individual data points.
You're passing just incrementing indices into the GL_ELEMENT_ARRAY_BUFFER, thus enumerating the vertices. Why? You can have this, without the extra work using glDrawArrays insteaf of glDrawElements.
You're deleting VertArray, thus keeping a dangling pointer.
Why didn't you just use the loop counter
i
?So how to fix this? Like this:
And using glDrawArrays; of course if you're not enumerating vertices, but have a list of faces→vertex indices, using a glDrawElements is mandatory.
不要为每个顶点调用
glBufferSubData
。它没有抓住 VBO 的要点。您应该为顶点数据创建大缓冲区,然后一次性将其传递给 OpenGL。阅读 http://www.opengl.org/sdk/docs/man/ xhtml/glVertexPointer.xml
当使用 VBO 时,这些指针与 VBO 数据相关。这就是为什么它通常为 0 或较小的偏移值。
stride = 0
表示数据是紧密打包的,OpenGL 可以根据其他参数计算stride
。我通常这样使用 VBO:
只需传递一个顶点数据块。然后使用
gl*Pointer
来描述如何使用 offsetof 宏。Don't call
glBufferSubData
for each vertex. It misses the point of VBO. You are supposed to create big buffer of your vertex data, and then pass it to OpenGL in a single go.Read http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml
When using VBOs those pointers are relative to VBO data. That's why it's usually 0 or small offset value.
stride = 0
means the data is tightly packed and OpenGL can calculate thestride
from other parameters.I usually use VBO like this:
Just pass a single chunk of vertex data. And then use
gl*Pointer
to describe how the data is packed using offsetof macro.要了解最后一个参数的偏移量,只需查看这篇文章......
GLES20.glVertexAttribPointer/glDrawElements 中的“偏移”参数是什么,以及ptr/索引从哪里来?
For knowing about the offset of the last parameter just look at this post....
What's the "offset" parameter in GLES20.glVertexAttribPointer/glDrawElements, and where does ptr/indices come from?