Как правильно заполнить Struct вершинами и индексным буфером для openGL ES

В настоящее время у меня проблемы с заполнением структуры вершинами для сетки (Screen Filling Quad с высоким разрешением)

Это структура:

    typedef struct {
    float Position[3];
    float Color[4];
    float TexCoord[2];
    }Vertex;

Обычно я бы просто заполнил его за руку, например

    const Vertex Vertices[]= {
    {{1,-1,0},{1,0,0,1},{1,1}},
    {{1,1,0},{0,1,0,1},{1,0}},
    {{-1,1,0},{0,0,1,1},{0,0}},
    {{-1,-1,0},{0,0,0,1},{0,1}}
    };

и связать это с моим буфером и т. д.

Поскольку мне нужна сетка с гораздо более высоким разрешением (сетки 11х11, которую я заполнил вручную, было недостаточно), я подумал о заполнении этого с помощью этого метода.

    - (void) createMesh : (int) width withHeight: (int)height{
        int size = width * height + height +1;
        Vertex Vert[]; //since we will be adding a vertex at the end for the (1,y), (1,v) and the x u

        int sizeInd = width * height * 2 * 3;
        GLubyte Ind[sizeInd]; // width * height * 2 number triangles, 3 indices per triangle

        float x,y,u,v;
        int count = 0;

        // Fill Vertices
        for (int i = 0 ; i <= height ; i++){

            y = ((1.0 - i/height) * -1.0) + ((i/height) * 1.0);
            v = 1.0 - (float) i / (float) height;

            for (int j = 0; j <= width; j++){

                x = (float) j / (float) width;
                u = x; //(float) j/ (float) count;

                //Vert[count]= {{x,y,0},{0,0,0,1.0},{u,v}}; 
                Vert[count].Position[0] = x;
                Vert[count].Position[1] = y;
                Vert[count].Position[2] = 0;

                Vert[count].Color[0] = 0.0;
                Vert[count].Color[1] = 0.0;
                Vert[count].Color[2] = 0.0;
                Vert[count].Color[3] = 1.0;

                Vert[count].TexCoord[0] = u;
                Vert[count].TexCoord[1] = v;
                count++;
            }
        }
        //Fill indices

        count = 0;
        for (int c = 0; c < sizeInd; c++){
            Ind[c] = count;
            c++;
            Ind[c] = count + 1;
            c++;
            Ind[c] = count + width + 1 + 1;
            c++;
            Ind[c] = count + 1;
            c++;
            Ind[c] = count + width + 1 + 1 + 1;
            c++;
            Ind[c] = count + width + 1 + 1;
            count++;
        }

        //Fill buffer
        glGenBuffers(1,&_vertexMeshBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vert), Vert, GL_STATIC_DRAW);

        glGenBuffers(1, &_indexMeshBuffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Ind), Ind, GL_STATIC_DRAW);

    } 

в моем методе рендеринга я рисую через

    glDrawElements(GL_TRIANGLES, (sizeof(GLubyte)* width * height * 2 * 3 / sizeof(GLubyte))/*sizeof(Ind)/sizeof(Ind[0])*/,
               GL_UNSIGNED_BYTE, 0);

так как GLubyte Ind не существует вне функции, я не могу получить размер через

    sizeof(Ind)/sizeof(Ind[0]) 

как я обычно делаю.

Итак, это правильный способ заполнить структуру или я должен сделать это как-нибудь еще? Этот подход мне кажется правильным, но это также может быть связано с тем, что в этом случае у меня недостаточно объективных знаний.

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

Или у кого-нибудь есть информация о том, как правильно настроить сетку с более высоким разрешением в объективе c / opengl?

Я уже реализовал алгоритм алмазного шага в C++, и я знаю, что этот подход не совсем закодирован, я просто упростил вызовы для моего личного использования.

Это больше о правильном использовании типа struct в сочетании с соответствующими вызовами gles.

любая помощь приветствуется.

РЕДАКТИРОВАТЬ:

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

Это должно быть правильно:

    //Fill indices

    count = 0;
    int jumpBarrier = 1;
    for (int c = 0; c < sizeInd; c++){
        Ind[c] = count;
        c++;
        Ind[c] = count + 1;
        c++;
        Ind[c] = count + width + 1; //; + 1;
        c++;
        Ind[c] = count + 1;
        c++;
        Ind[c] = count + width + 1 + 1;// + 1;
        c++;
        Ind[c] = count + width + 1 ;//+ 1;

        //jump
        if (jumpBarrier == width){
            count++;
            count++;
            jumpBarrier = 1;
        }
        else{
            count++;
            jumpBarrier++;
        }
    }

РЕДАКТИРОВАТЬ № 2

Дух, внутренний цикл for увеличил i вместо j, исправил это, вершины и индексы теперь создаются так, как они должны быть.

РЕДАКТИРОВАТЬ № 3

Поэтому мне удалось обойти эту проблему, используя модификацию этого алгоритма, чтобы просто напечатать все в текстовом файле, а затем скопировать все в моем проекте, где они мне нужны.

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

1 ответ

Решение

О создании вашей сетки:

Начнем с того, что вы действительно усложняете свой самый простой код. Также некоторые комментарии помогут вам в отладке вашего кода. Посмотрите на этот римейк:

- (void)createMesh:(int)width height:(int)height {
    int horizontalVertexCount = width+1; // one more then the number of surfaces
    int verticalVertexCount = height+1; // one more then the number of surfaces

    int vertexCount = horizontalVertexCount * verticalVertexCount; // a number of unique vertices

    // generate a buffer
    size_t vertexBufferSize = sizeof(Vertex)*vertexCount;
    Vertex *vertexBuffer = malloc(vertexBufferSize);

    // iterate through vertices and fill them
    for(int h=0; h<verticalVertexCount; h++) {

        // generate y position coordinate

        GLfloat y = h;
        y /= verticalVertexCount; // normalzie to be in range of [0,1]
        y = 2.0f*y - 1; // converting the range [0,1] to [-1,1]

        // generate y texture coordinate

        GLfloat v = h;
        v /= verticalVertexCount; // normalzie to be in range of [0,1]
        v = 1.0f - v; // converting the range [0,1] to [1,0]

        for(int w=0; h<horizontalVertexCount; w++) {
            // generate x position coordinate

            GLfloat x = w;
            x /= horizontalVertexCount; // normalzie to be in range of [0,1]

            // generate x texture coordinate

            GLfloat u = x;

            /*
             The next segment may be replaced by using access as:

             vertexBuffer[h*horizontalVertexCount + w].Position[0] = x;
             */

            Vertex *currentVertex = vertexBuffer + h*horizontalVertexCount + w;
            currentVertex->Position[0] = x;
            currentVertex->Position[1] = y;
            currentVertex->Position[2] = .0f;

            currentVertex->Color[0] = .0f;
            currentVertex->Color[1] = .0f;
            currentVertex->Color[2] = .0f;
            currentVertex->Color[3] = 1.0f;

            currentVertex->TexCoord[0] = u;
            currentVertex->TexCoord[1] = v;
        }
    }

    // create index buffer
    int numberOfSurfaces = width*height;
    int indicesPerSurface = 2*3; // 2 triangles per surface
    size_t indexBufferSize = sizeof(GLushort)*numberOfSurfaces*indicesPerSurface;
    GLushort *indexBuffer = malloc(indexBufferSize);

    for(int h=0; h<height; h++) {
        for(int w=0; w<width; w++) {
            int surfaceIndex = h*width + w;
            int firstIndexIndex = surfaceIndex*indicesPerSurface;

            indexBuffer[firstIndexIndex] = h*horizontalVertexCount + w; // upper left
            indexBuffer[firstIndexIndex] = h*horizontalVertexCount + (w + 1); // upper right
            indexBuffer[firstIndexIndex] = (h+1)*horizontalVertexCount + w; // lower left

            indexBuffer[firstIndexIndex] = h*horizontalVertexCount + (w + 1); // upper right
            indexBuffer[firstIndexIndex] = (h+1)*horizontalVertexCount + (w+1); // lower right
            indexBuffer[firstIndexIndex] = (h+1)*horizontalVertexCount + w; // lower left
        }
    }

    //Fill buffer
    glGenBuffers(1,&_vertexMeshBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexMeshBuffer);
    glBufferData(GL_ARRAY_BUFFER, vertexBufferSize, vertexBuffer, GL_STATIC_DRAW);

    glGenBuffers(1, &_indexMeshBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexMeshBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, GL_STATIC_DRAW);

    // release the memory
    free(vertexBuffer);
    free(indexBuffer);
}

Я не уверен, что код, который я разместил, работает правильно, но его очень легко отладить с помощью простой точки останова, если это не так. Не пытайтесь оптимизировать свой код до тех пор, пока вам не понадобится то, что в основном происходит после того, как работает текущий код.

Обратите внимание на несколько вещей в этом примере:

  • malloc используется для создания буфера в памяти. Это обязательно для больших наборов данных, иначе ваш стек может переполниться.
  • Индексный буфер используется как short потому что byte может хранить только до 256 уникальных индексов, поэтому ваш код в любом случае не будет работать с большими сетками. (Большие типы, чем короткие, обычно не поддерживаются для индексации)
  • Некоторый код, такой как доступ к вершинам по указателю, предназначен только для демонстрации. Вы можете изменить их на прямой доступ.

О Vertex :

Я настаиваю на том, что в языках Си такая структура данных выполнена, как вы, но вы могли бы продолжить немного дальше. position, color а также texture координаты должны быть structures, И поддерживать более естественный доступ к ценностям. Примерно так должно быть интересно

typedef union {
    struct {
        GLfloat x, y;
    };
    struct {
        GLfloat u, v;
    };
}Vector2f;

typedef struct {
    GLfloat x, y, z;
}Vector3f;

typedef union {
    struct {
        Vector3f vector3f;
        GLfloat _w;
    };
    struct {
        GLfloat x, y, z, w;
    };
    struct {
        GLfloat r, g, b, a;
    };
}Vector4f;

typedef struct {
    Vector3f position;
    Vector4f color;
    Vector2f textureCoordinates;
}Vertex;

Если вы не привыкли к понятию union Я предлагаю вам включить это в свой список TO-READ-ABOUT. В этом случае он позволяет вам получить доступ к одним и тем же данным через несколько свойств: если у вас есть Vector4f myVector затем myVector.x всегда так же, как myVector.r, это сделано для того, чтобы код был понятнее того, что вы делаете. Вы можете видеть, что GLSL (шейдерный язык) использует те же принципы. Использование может быть:

            currentVertex->position.x = x;
            currentVertex->position.y = y;
            currentVertex->position.z = .0f;

            currentVertex->color.r = .0f; // could use .x
            currentVertex->color.g = .0f; // could use .y
            currentVertex->color.b = .0f; // could use .z
            currentVertex->color.a = 1.0f; // could use .w

После того, как у вас есть хорошая структура, все, что вам когда-либо понадобится sizeof (вы уже используете его) и offsetof (который, я надеюсь, вы используете при назначении указателей на атрибуты). При этом вам не нужно будет изменять код чертежа, если вы редактируете свою структуру. Например, если в какой-то момент вы решили добавить нормали в ваш Vertex структура, вы просто добавите еще один вектор в структуру, и никакой другой код не нужно менять, чтобы ваша программа работала.

Упаковка данных:

Поэтому после того, как у вас есть ваши структуры, вы захотите упаковать их в некоторые объекты. На этом этапе я предлагаю вам перейти к полной версии Objective-C и создать новый класс, который содержит все данные, необходимые для рисования объекта. Это означает, что у вас будут идентификаторы буфера вершин и индексного буфера, режима рисования, смещений, шага и количества вершин. Базовая реализация должна выглядеть примерно так:

@interface MeshVertexObject : NSObject

@property (nonatomic) GLuint vertexBufferID; // assigned when generating a mesh
@property (nonatomic) GLuint indexBufferID; // assigned when generating a mesh
@property (nonatomic) GLuint vertexCount; // assigned when generating a mesh
@property (nonatomic) GLenum drawMode; // assigned when generating a mesh. GL_TRIANGLES

@property (nonatomic, readonly) void *positionPointer; // depending on the Vertex structure
@property (nonatomic, readonly) void *colorPointer; // depending on the Vertex structure
@property (nonatomic, readonly) void *texturePointer; // depending on the Vertex structure

@property (nonatomic, readonly) GLint positionStride; // depending on the Vertex structure
@property (nonatomic, readonly) GLint colorStride; // depending on the Vertex structure
@property (nonatomic, readonly) GLint textureStride; // depending on the Vertex structure


@end

@implementation MeshVertexObject

- (void)dealloc {
    // remove all the GL data bound to this calss on destructor
    if(self.vertexBufferID > 0) {
        GLuint vBuffer = self.vertexBufferID;
        self.vertexBufferID = 0;
        glDeleteBuffers(1, &vBuffer);
    }
    if(self.indexBufferID > 0) {
        GLuint iBuffer = self.indexBufferID;
        self.indexBufferID = 0;
        glDeleteBuffers(1, &iBuffer);
    }
}

- (void *)positionPointer {
    return (void *)offsetof(Vertex, position);
}
- (void *)colorPointer {
    return (void *)offsetof(Vertex, color);
}
- (void *)texturePointer {
    return (void *)offsetof(Vertex, textureCoordinates);
}

- (GLint)positionStride {
    return sizeof(Vertex);
}
- (GLint)colorStride {
    return sizeof(Vertex);
}
- (GLint)textureStride {
    return sizeof(Vertex);
}

@end

Теперь это класс, который будет содержать ваш метод генерации сетки и, возможно, будет включать другие генераторы формы, такие как кубы, сферы...

Но затем кроличья нора становится еще глубже... Теперь, когда вы можете построить систему в системе, вы можете создавать более сложные объекты, которые также включают идентификаторы текстуры (или, скорее, скорее Texture объекты, которые включают эти идентификаторы) и объекты сетки. Затем также добавьте данные для их расположения и ориентации на сцене и удобства для создания матриц, которые вы используете при рисовании конвейера...

Вывод:

Вы делаете это правильно или, по крайней мере, идете правильным путем. Но если вы хотите продолжить сборку системы, если вы хотите иметь возможность отлаживать свой код и, в основном, поддерживать свой код, тогда держите модули как можно более простыми и прокомментированными, насколько это возможно, и просто продолжайте строить вверх.

Другие вопросы по тегам