学习正确使用 VBO

发布于 2024-12-10 11:22:40 字数 3031 浏览 0 评论 0原文

因此,我一直在尝试自学使用 VBO,以提高 OpenGL 项目的性能并学习比固定功能渲染更高级的东西。但我还没有找到太多像样的教程;到目前为止,我发现的最好的教程是 Songho 的教程 以及 OpenGL.org,但我似乎缺少某种背景知识来完全理解正在发生的事情我无法确切地说出我没有得到什么,除了使用一些参数之外。

无论如何,我已经继续前进并提出了一些至少不会崩溃的代码,但它会导致奇怪的结果。我想要渲染的是这个(使用固定功能渲染;它应该是棕色和背景灰色,但我所有的OpenGL屏幕截图似乎都采用洋红色作为他们最喜欢的颜色;也许是因为我对窗口使用SFML?)。

Target image

不过,我得到的是:

实际图像

我不知所措。这是我使用的相关代码,首先用于设置缓冲区对象(我根据 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?).

Target image

What I get, though, is this:

Actual image

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

狠疯拽 2024-12-17 11:22:40

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

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

现在让我们看一下您的代码

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。

The initial value is said to be 0; but it's supposed to be a pointer.

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

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

You shouldn't really mix STL containers and new, learn about the RAII pattern.

pWorld->CreateVertexArray(VertArray);

This is problematic, since you'll delete VertexArray later on, leaving you with a dangling pointer. Not good.

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

You should submit large batches of data with glBufferSubData, not individual data points.

    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, i * sizeof(unsigned short), sizeof(unsigned short), &(Indice));

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.

    ++Indice;
}
delete VertArray;

You're deleting VertArray, thus keeping a dangling pointer.

Indice -= 1;

Why didn't you just use the loop counter i?

So how to fix this? Like this:

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

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

And using glDrawArrays; of course if you're not enumerating vertices, but have a list of faces→vertex indices, using a glDrawElements is mandatory.

烟花肆意 2024-12-17 11:22:40

不要为每个顶点调用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 宏。

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 the stride from other parameters.

I usually use VBO like this:

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

Just pass a single chunk of vertex data. And then use gl*Pointer to describe how the data is packed using offsetof macro.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文