【问题标题】:OpenGL ES Interleaving Vertex Buffer ObjectOpenGL ES 交错顶点缓冲区对象
【发布时间】:2011-12-30 18:33:09
【问题描述】:

我一直在研究适用于 iOS 的新 OpenGL 框架,恰当地命名为 GLKit,并且一直在尝试将一些现有的 OpenGL 1.0 代码移植到 OpenGL ES 2.0,只是为了让我的脚趾浸入水中并掌握事物.

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

read on SO a while ago 谈到了使用 NSMutableData 优于经典 malloc/free 的好处,并想在编写我的 VBO 时尝试采用这种方法。到目前为止,我已经设法将一个 sn-p 拼凑在一起,看起来我正朝着正确的方向前进,但我并不完全确定 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 目前或更确切地说,对实施而不是至少对概念感到困惑。如果有人愿意关注我的实现,那将是非常棒的,因为我确信我在此过程中的某个地方犯了一个菜鸟错误,但你知道当你盯着某样东西看几个小时是怎么回事没有进展就结束了。

感谢阅读!

【问题讨论】:

  • 我猜没有人愿意参与其中。无论如何,我已经更新了代码,以防万一将来有人路过。
  • 对不起,没有人回答你的问题(主要是因为这对我也有很大的帮助......)。
  • 我分享您对“实施,而不是概念”的困惑。昨天问了一个关于同一主题的问题(stackoverflow.com/questions/8430108/…),这个问题比你的要基本得多,所以我认为这个问题的唯一答案对你没有帮助……看看吧。
  • 您可能应该在下次标记此 OpenGL 并完全忽略整个移动部分,只说“交错 VBO 初始化/渲染代码无法正常工作”,然后发布相关代码。你会得到更多的关注。人们被 Objective-c 和 iOS 标签吓跑了,因为他们认为自己无法弄清楚。

标签: objective-c opengl-es ios5 glkit


【解决方案1】:

我想我看到了你的问题。

你有一个结构,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,或者在某处跟踪实际的索引总数。

【讨论】:

  • 感谢您的回复!我刚刚尝试了快速简便的修复,并且可以确认它有效:) 我将修改代码以包含您提到的索引总数,因为这将更有意义(并且看起来也更干净)。再次感谢!
  • @arasmussn,请你解释一下为什么你将(void *)0传递给glDrawElements的最后一个参数?
  • @kelin:在本例中,我们将索引存储在缓冲区对象 (buffers[1]) 中。 glDrawElements 的最后一个参数被解释为当前绑定到GL_ELEMENT_ARRAY_BUFFER 的缓冲区的偏移量。类似于glVertexAttribPointer 的工作原理。
  • @arasmussen,但documentation 表示应该引用索引数组。我是不是误会了什么?
  • 您的链接无效。看看这个页面:opengl.org/wiki/GLAPI/glDrawElements.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多