OpenGL ES 交错顶点缓冲区对象

发布于 2024-12-15 16:46:34 字数 7392 浏览 2 评论 0原文

我一直在研究适用于 iOS 的新 OpenGL 框架(恰当地命名为 GLKit),并且一直在尝试将一些现有的 OpenGL 1.0 代码移植到 OpenGL ES 2.0,只是为了尝试一下并掌握一些东西。

在阅读了 API 以及 Apple 和 OpenGL 文档提供的大量其他最佳实践之后,我已经根深蒂固地认为我应该使用顶点缓冲区对象并使用“元素”或更确切地说,顶点索引。似乎有很多提到通过在必要时使用填充来优化内存存储,但这也许是另一天的对话;)

不久前阅读过了解使用的好处NSMutableData 优于经典的 malloc/free,并且想在编写我的 VBO 时尝试采用这种方法。到目前为止,我已经成功地拼凑了一个片段,看起来我正走在正确的轨道上,但我不完全确定 VBO 应该包含多少数据。目前

//import headers
#import <GLKit/GLKit.h>

#pragma mark -
#pragma mark InterleavingVertexData

//vertex buffer object struct
struct InterleavingVertexData
{
    //vertices
    GLKVector3 vertices;

    //normals
    GLKVector3 normal;

    //color
    GLKVector4 color;

    //texture coordinates
    GLKVector2 texture;
};
typedef struct InterleavingVertexData InterleavingVertexData;

#pragma mark -
#pragma mark VertexIndices

//vertex indices struct
struct VertexIndices
{
    //vertex indices
    GLuint a;
    GLuint b;
    GLuint c;
};
typedef struct VertexIndices VertexIndices;

//create and return a vertex index with specified indices
static inline VertexIndices VertexIndicesMake(GLuint a, GLuint b, GLuint c)
{
    //declare vertex indices
    VertexIndices vertexIndices;

    //set indices
    vertexIndices.a = a;
    vertexIndices.b = b;
    vertexIndices.c = c;

    //return vertex indices
    return vertexIndices;
}

#pragma mark -
#pragma mark VertexBuffer

//vertex buffer struct
struct VertexBuffer
{
    //vertex data
    NSMutableData *vertexData;

    //vertex indices
    NSMutableData *indices;

    //total number of vertices
    NSUInteger totalVertices;

    //total number of indices
    NSUInteger totalIndices;
};
typedef struct VertexBuffer VertexBuffer;

//create and return a vertex buffer with allocated data
static inline VertexBuffer VertexBufferMake(NSUInteger totalVertices, NSUInteger totalIndices)
{
    //declare vertex buffer
    VertexBuffer vertexBuffer;

    //set vertices and indices count
    vertexBuffer.totalVertices = totalVertices;
    vertexBuffer.totalIndices = totalIndices;

    //set vertex data and indices
    vertexBuffer.vertexData = nil;
    vertexBuffer.indices = nil;

    //check vertices count
    if(totalVertices > 0)
    {
        //allocate data
        vertexBuffer.vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
    }

    //check indices count
    if(totalIndices > 0)
    {
        //allocate data
        vertexBuffer.indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
    }

    //return vertex buffer
    return vertexBuffer;
}

//grow or shrink a vertex buffer
static inline void VertexBufferResize(VertexBuffer *vertexBuffer, NSUInteger totalVertices, NSUInteger totalIndices)
{
    //check adjusted vertices count
    if(vertexBuffer->totalVertices != totalVertices && totalVertices > 0)
    {
        //set vertices count
        vertexBuffer->totalVertices = totalVertices;

        //check data is valid
        if(vertexBuffer->vertexData)
        {
            //allocate data
            [vertexBuffer->vertexData setLength:(sizeof(InterleavingVertexData) * totalVertices)];
        }
        else
        {
            //allocate data
            vertexBuffer->vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
        }
    }

    //check adjusted indices count
    if(vertexBuffer->totalIndices != totalIndices && totalIndices > 0)
    {
        //set indices count
        vertexBuffer->totalIndices = totalIndices;

        //check data is valid
        if(vertexBuffer->indices)
        {
            //allocate data
            [vertexBuffer->indices setLength:(sizeof(VertexIndices) * totalIndices)];
        }
        else
        {
            //allocate data
            vertexBuffer->indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
        }
    }
}

//release vertex buffer data
static inline void VertexBufferRelease(VertexBuffer *vertexBuffer)
{
    //set vertices and indices count
    vertexBuffer->totalVertices = 0;
    vertexBuffer->totalIndices = 0;

    //check vertices are valid
    if(vertexBuffer->vertexData)
    {
        //clean up
        [vertexBuffer->vertexData release];
        vertexBuffer->vertexData = nil;
    }

    //check indices are valid
    if(vertexBuffer->indices)
    {
        //clean up
        [vertexBuffer->indices release];
        vertexBuffer->indices = nil;
    }
}

,交错顶点数据包含足够的内容来存储每个顶点的顶点、法线、颜色和纹理坐标。我的印象是顶点和索引的数量相同,但实际上情况显然并非如此,因此,索引是 VBO 的一部分,而不是 InterleavingVertexData 的一部分。

问题已更新:

在设法将上面的代码调整为工作状态后,我已经更新了它。希望它对将来的人有用。

现在我已经成功设置了所有内容,但我在渲染绑定到 VBO 的内容时无法获得预期结果。以下是我到目前为止将数据加载到 OpenGL 中的代码:

//generate buffers
glGenBuffers(2, buffers);

//bind vertices buffer
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, (sizeof(InterleavingVertexData) * vertexBuffer.totalVertices), self.vertexData, GL_STATIC_DRAW);

//bind indices buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);

//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

以及渲染所有内容的代码:

//enable required attributes
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glEnableVertexAttribArray(GLKVertexAttribColor);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);

//bind buffers
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);

//set shape attributes
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, vertices));
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, normal));
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, color));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, texture));

//draw shape
glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);

//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

//disable atttributes
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
glDisableVertexAttribArray(GLKVertexAttribColor);
glDisableVertexAttribArray(GLKVertexAttribNormal);
glDisableVertexAttribArray(GLKVertexAttribPosition);

虽然我的 iPhone 还没有爆炸出令人惊叹的独角兽从眼睛里射出彩虹的图形,但我还无法渲染整体上是一个简单的形状,而不会把我的头发扯下来。

从渲染来看,似乎只绘制了每个形状的 1/3,也许是 1/2,具体取决于视角。似乎罪魁祸首是传递给 glDrawElements 的 count 参数,因为摆弄它有不同的结果,但我已经阅读了文档并一遍又一遍地检查了该值,它确实期望索引的总数(这就是我想要的)目前正在通过)。

正如我在最初的问题中提到的,我目前对 VBO 感到非常困惑,或者更确切地说,对实现感到困惑,而不是至少对概念感到困惑。如果有人愿意关注我的实现,那就太棒了,因为我确信我在这个过程中的某个地方犯了菜鸟错误,但当你盯着某个东西几个小时时你知道它是怎么回事毫无进展地结束。

感谢您的阅读!

I've been looking at the new OpenGL framework for iOS, aptly named GLKit, and have been playing around with porting some existing OpenGL 1.0 code to OpenGL ES 2.0 just to dip my toe in the water and get to grips with things.

After reading the API and a whole ream of other best practices provided by Apple and the OpenGL documentation, i've had it pretty much ingrained into me that I should be using Vertex Buffer Objects and using "elements" or rather, vertex indices. There seems to be a lot of mention of optimising memory storage by using padding where necessary too but that's a conversation for another day perhaps ;)

I read on SO a while ago about the benefits of using NSMutableData over classic malloc/free and wanted to try and take this approach when writing my VBO. So far i've managed to bungle together a snippet that looks like i'm heading down the right track but i'm not entirely sure about how much data a VBO should contain. Here's what i've got so far:

//import headers
#import <GLKit/GLKit.h>

#pragma mark -
#pragma mark InterleavingVertexData

//vertex buffer object struct
struct InterleavingVertexData
{
    //vertices
    GLKVector3 vertices;

    //normals
    GLKVector3 normal;

    //color
    GLKVector4 color;

    //texture coordinates
    GLKVector2 texture;
};
typedef struct InterleavingVertexData InterleavingVertexData;

#pragma mark -
#pragma mark VertexIndices

//vertex indices struct
struct VertexIndices
{
    //vertex indices
    GLuint a;
    GLuint b;
    GLuint c;
};
typedef struct VertexIndices VertexIndices;

//create and return a vertex index with specified indices
static inline VertexIndices VertexIndicesMake(GLuint a, GLuint b, GLuint c)
{
    //declare vertex indices
    VertexIndices vertexIndices;

    //set indices
    vertexIndices.a = a;
    vertexIndices.b = b;
    vertexIndices.c = c;

    //return vertex indices
    return vertexIndices;
}

#pragma mark -
#pragma mark VertexBuffer

//vertex buffer struct
struct VertexBuffer
{
    //vertex data
    NSMutableData *vertexData;

    //vertex indices
    NSMutableData *indices;

    //total number of vertices
    NSUInteger totalVertices;

    //total number of indices
    NSUInteger totalIndices;
};
typedef struct VertexBuffer VertexBuffer;

//create and return a vertex buffer with allocated data
static inline VertexBuffer VertexBufferMake(NSUInteger totalVertices, NSUInteger totalIndices)
{
    //declare vertex buffer
    VertexBuffer vertexBuffer;

    //set vertices and indices count
    vertexBuffer.totalVertices = totalVertices;
    vertexBuffer.totalIndices = totalIndices;

    //set vertex data and indices
    vertexBuffer.vertexData = nil;
    vertexBuffer.indices = nil;

    //check vertices count
    if(totalVertices > 0)
    {
        //allocate data
        vertexBuffer.vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
    }

    //check indices count
    if(totalIndices > 0)
    {
        //allocate data
        vertexBuffer.indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
    }

    //return vertex buffer
    return vertexBuffer;
}

//grow or shrink a vertex buffer
static inline void VertexBufferResize(VertexBuffer *vertexBuffer, NSUInteger totalVertices, NSUInteger totalIndices)
{
    //check adjusted vertices count
    if(vertexBuffer->totalVertices != totalVertices && totalVertices > 0)
    {
        //set vertices count
        vertexBuffer->totalVertices = totalVertices;

        //check data is valid
        if(vertexBuffer->vertexData)
        {
            //allocate data
            [vertexBuffer->vertexData setLength:(sizeof(InterleavingVertexData) * totalVertices)];
        }
        else
        {
            //allocate data
            vertexBuffer->vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
        }
    }

    //check adjusted indices count
    if(vertexBuffer->totalIndices != totalIndices && totalIndices > 0)
    {
        //set indices count
        vertexBuffer->totalIndices = totalIndices;

        //check data is valid
        if(vertexBuffer->indices)
        {
            //allocate data
            [vertexBuffer->indices setLength:(sizeof(VertexIndices) * totalIndices)];
        }
        else
        {
            //allocate data
            vertexBuffer->indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
        }
    }
}

//release vertex buffer data
static inline void VertexBufferRelease(VertexBuffer *vertexBuffer)
{
    //set vertices and indices count
    vertexBuffer->totalVertices = 0;
    vertexBuffer->totalIndices = 0;

    //check vertices are valid
    if(vertexBuffer->vertexData)
    {
        //clean up
        [vertexBuffer->vertexData release];
        vertexBuffer->vertexData = nil;
    }

    //check indices are valid
    if(vertexBuffer->indices)
    {
        //clean up
        [vertexBuffer->indices release];
        vertexBuffer->indices = nil;
    }
}

Currently, the interleaving vertex data contains enough to store the vertices, normals, colors and texture coordinates for each vertex. I was under the impression that there would be an equal number of vertices and indices but in practice this obviously isn't the case so for this reason, the indices are part of the VBO rather than the InterleavingVertexData.

Question Updated:

I've updated the code above after managing to wrangle it into a working state. Hopefully it will come in useful to someone in the future.

Now that i've managed to set everything up, i'm having trouble getting the expected results from rendering the content bound to the VBO. Here's the code i've got so far for loading my data into OpenGL:

//generate buffers
glGenBuffers(2, buffers);

//bind vertices buffer
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, (sizeof(InterleavingVertexData) * vertexBuffer.totalVertices), self.vertexData, GL_STATIC_DRAW);

//bind indices buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);

//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

And the code for rendering everything:

//enable required attributes
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glEnableVertexAttribArray(GLKVertexAttribColor);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);

//bind buffers
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);

//set shape attributes
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, vertices));
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, normal));
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, color));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, texture));

//draw shape
glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);

//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

//disable atttributes
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
glDisableVertexAttribArray(GLKVertexAttribColor);
glDisableVertexAttribArray(GLKVertexAttribNormal);
glDisableVertexAttribArray(GLKVertexAttribPosition);

Whilst my iPhone hasn't yet exploded with awesome graphics of unicorns shooting rainbows from their eyes, I haven't been able to render a simple shape in it's entirety without tearing my hair out.

From the rendering it looks as though only 1/3rd of each shape is being drawn, perhaps 1/2 depending on the viewing angle. It seems the culprit it the count parameter passed to glDrawElements as fiddling with this has differing results but I've read the documentation and checked the value over and over again and it does indeed expect the total number of indices (which is what i'm passing currently).

As I mentioned in my original question, i'm quite confused by VBO's currently or rather, confused by the implementation rather than the concept at least. If anyone would be so kind as to cast an eye over my implementation, that would be super awesome as i'm sure i've made a rookie error somewhere along the way but you know how it is when you stare at something for hours on end with no progress.

Thanks for reading!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

眉黛浅 2024-12-22 16:46:34

我想我看到了你的问题。

您有一个结构体 VertexIndices,其中包含三个索引,或一个三角形的索引。当您绑定 IBO(索引缓冲区对象,包含索引的缓冲区对象)时,您可以执行以下操作:

glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);

这很好。 glBufferData 中的 size 参数以字节为单位,因此您需要将 sizeof(3 floats) 乘以您拥有的 3 个浮点组的数量。伟大的。

但是,当您实际调用 glDrawElements 时,您会这样做:

glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);

但是,vertexBuffer.totalIndices 等于您拥有的 VertexIndices 结构的数量,这等于索引总数/3(或三角形总数)。因此,您需要执行以下操作之一:

  1. 简单修复但愚蠢:glDrawElements(..., vertexBuffer.totalIndices * 3, ...);
  2. 适当的更多工作:vertexBuffer.totalIndices 应包含您拥有的实际索引总数,而不是您正在渲染的三角形总数。

您需要执行其中一项操作,因为现在 TotalIndices 包含您拥有的 VertexIndices 总数,并且每个索引都有 3 个索引。这里正确的做法是将totalIndices重命名为totalTriangles,或者在某处跟踪索引的实际总数。

I think I see your problem.

You've got a struct, VertexIndices which contains three indices, or the indices for one triangle. When you bind your IBO (Index Buffer Object, the buffer object containing your indices), you do this:

glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);

Which is fine. The size parameter in glBufferData is in bytes so you're multiplying sizeof(3 floats) by the number of groups of 3 floats that you have. Great.

But then when you actually call glDrawElements, you do this:

glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);

However, the vertexBuffer.totalIndices is equal to the number of VertexIndices structs you've got, which is equal to the total number of indices / 3 (or total number of triangles). So you need to do one of the following:

  1. Easy fix yet stupid: glDrawElements(..., vertexBuffer.totalIndices * 3, ...);
  2. Proper yet more work: vertexBuffer.totalIndices should contain the actual total number of indices that you've got, not the total number of triangles you're rendering.

You need to do one of these because right now totalIndices contains the total number VertexIndices you've got, and each one has 3 indices. The right thing to do here is either rename totalIndices to totalTriangles, or keep track of the actual total number of indices somewhere.

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