OpenGL ES чередующийся объект буфера вершин - PullRequest
11 голосов
/ 17 ноября 2011

Я смотрел на новую платформу OpenGL для iOS, точно названную GLKit, и пытался перенести существующий код OpenGL 1.0 на OpenGL ES 2.0, чтобы просто погрузиться в дело и разобраться с вещами. .

После прочтения API и множества других передовых методов, предоставленных Apple, и документации OpenGL я понял, что мне следует использовать объекты буфера вершин и использовать "элементы" или, вернее, вершины индексы. Кажется, есть много упоминаний об оптимизации хранения памяти с помощью заполнения там, где это необходимо, но, возможно, это разговор на другой день;)

Я читал на SO некоторое время назад о преимуществах использования 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 в зависимости от угла обзора. Кажется, виновником является то, что параметр count, переданный glDrawElements, так как с этим у него разные результаты, но я прочитал документацию и проверил значение снова и снова, и он действительно ожидает общее количество индексов (что я и делаю прохожу сейчас).

Как я уже упоминал в своем первоначальном вопросе, я весьма смущен тем, что VBO в настоящее время или, скорее, смущает реализация, а не концепция, по крайней мере. Если бы кто-то был так любезен, чтобы взглянуть на мою реализацию, это было бы супер здорово, так как я уверен, что где-то по пути сделал ошибку новичка, но вы знаете, как это происходит, когда вы часами смотрите на что-то конец без прогресса.

Спасибо за чтение!

1 Ответ

4 голосов
/ 16 декабря 2011

Мне кажется, я вижу твою проблему.

У вас есть структура, VertexIndices, которая содержит три индекса или индексы для одного треугольника. Когда вы связываете свой IBO (объект буфера индекса, объект буфера, содержащий ваши индексы), вы делаете это:

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

Что хорошо. Параметр size в glBufferData указан в байтах, поэтому вы умножаете sizeof (3 числа с плавающей запятой) на количество групп из 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, либо где-то отслеживать фактическое общее количество индексов.

...