2012-01-06 3 views
9

Я изучаю OpenGL в течение нескольких дней, следуя некоторым учебным пособиям и кодируя собственные собственные эксперименты. Но есть одна вещь, которую я действительно не понимаю, что мешает мне продолжать. Я уже несколько часов собираюсь поработать в Интернете и не нашел ответа на свой вопрос.OpenGLES 2.0 отдельные буферы для вершин, цветов и координат текстуры

Где я должен указывать каждое отдельное значение цвета и координату текстуры для каждой отдельной вершины? Должны ли эти свойства всегда указывать в том же массиве (struct), что и позиции вершин? Вроде так:

const Vertex Vertices[] = { 
    // Front 
    {{1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}}, 
    {{1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}}, 
    {{-1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}}, 
    {{-1, -1, 0}, {0, 0, 0, 1}, {0, 0}}, 

    ... 

Или есть способ установить значения цвета и координаты текстуры в отдельных массивах? Но тогда возникает вопрос: как я могу позвонить glDrawElements с отдельными массивами?

В случае, если вам интересно, почему я хочу отделить эти значения: в настоящее время я создаю свой собственный .obj-парсер в obj-c, и мне было интересно: что делать, если вы загружаете модель без текстуры и хотите показать только цвет на объекте? Или: что, если вы хотите загрузить модель с только текстурой, сопоставленной с ней, но без отдельного цвета на вершину? И: Не помещает значения цвета и координаты текстуры, раздувая структуру Vertex со слишком большим количеством данных.

ответ

21

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

Последний раз, когда я вступил в контакт с ES 2.0, был в контексте WebGL (который имеет немного отличающуюся спецификацию, но в конечном итоге основан на ES 2.0).

Что в основном сделано, запись данных в буфер с использованием раздельного

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), positions, GL_STATIC_DRAW); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), colors, GL_STATIC_DRAW); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

... 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort), indices, GL_STATIC_DRAW); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

с позициями и цветами, являющихся поплавком массивы, содержащих данные вершин и индексы, содержащих показатели, как неподписанные шорты в этом случае.

Для визуализации этих данных, нужно использовать буферы и приписывать указатели на ваш шейдер:

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glVertexAttribPointer(vertexPositionAttribute, 3, GL_FLOAT, false, 0, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glVertexAttribPointer(vertexColorAttribute, 4, GL_FLOAT, false, 0, 0); 

Наконец связать индекс буфера:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 

и визуализации:

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0); 

Чтобы получить следующие атрибуты:

glUseProgram(shaderProgram); 

vertexPositionAttribute= glGetAttribLocation(shaderProgram, "vertexPosition"); 
glEnableVertexAttribArray(vertexPositionAttribute); 

vertexColorAttribute = glGetAttribLocation(shaderProgram, "vertexColor"); 
glEnableVertexAttribArray(vertexColorAttribute); 

... 

Если вы не имеете пользовательские шейдеры (используя фиксированную функцию), вы можете быть в состоянии использовать

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glVertexPointer(3, GL_FLOAT, false, 0, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glColorPointer(4, GL_FLOAT, false, 0, 0); 

вместо этого. Я бы посоветовал это сделать, поскольку он устарел (если вообще доступен в ES 2.0). Если вы все еще хотите использовать его, вы можете пропустить весь буфер бизнес вообще и использовать

glVertexPointer(3, GL_FLOAT, false, 0, positions); 
glColorPointer(4, GL_FLOAT, false, 0, colors); 

с

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, indices); 

Я надеюсь, что это не слишком запутанной и помогает немного. Для дальнейшего чтения, хотя и нацелен на OpenGL, я бы предложил Nehe tutorials.

+0

Кристально чистый! ;) – polyclick

0

Вы можете разделить их на суббуферы, но если вы их используете, вы должны иметь их для всех вершин, и если вы используете индексы-буферы, тогда вы должны использовать один индекс-буфер для всех (положение, цвет, texcoord и т. д.). Вот некоторые выдержки из моего кода:

распределение с

glBindBuffer(GL_ARRAY_BUFFER, mId); 
glBufferData(GL_ARRAY_BUFFER, 
       mMaxNumberOfVertices * (mVertexBlockSize + mNormalBlockSize + mColorBlockSize + mTexCoordBlockSize), 
       0, 
       mDrawMode); 

заправить

glBufferSubData(GL_ARRAY_BUFFER, mVertexOffset, numberOfVertsToStore * mVertexBlockSize, vertices); 
glBufferSubData(GL_ARRAY_BUFFER, mNormalOffset, numberOfVertsToStore * mNormalBlockSize, normals); 
glBufferSubData(GL_ARRAY_BUFFER, mColorOffset, numberOfVertsToStore * mColorBlockSize, colors); 
glBufferSubData(GL_ARRAY_BUFFER, mTexCoordOffset, numberOfVertsToStore * mTexCoordBlockSize, texCoords); 

и использование с этим (я не думаю, что постоянно коммутационный clientStates является лучшей практикой, хотя)

void Vbo::draw(GLenum primMode) 
{ 
    glEnableClientState(GL_VERTEX_ARRAY); 
    glVertexPointer(mVertexComponents, GL_FLOAT, 0, (void*)mVertexOffset); 

    if(mNormalBlockSize){ 
    glEnableClientState(GL_NORMAL_ARRAY); 
    glNormalPointer(GL_FLOAT, 0, (void*)mNormalOffset); 
    } 
    if(mColorBlockSize){ 
    glEnableClientState(GL_COLOR_ARRAY); 
    glColorPointer(mColorComponents, GL_FLOAT, 0, (void*)mColorOffset); 
    } 
    if(mTexCoordBlockSize){ 
    glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
    glTexCoordPointer(mTexCoordComponents, GL_FLOAT, 0, (void*)mTexCoordOffset); 
    } 

    if (mAttachedIndexBuffer) 
    { 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mAttachedIndexBuffer); 
    glDrawElements(primMode, 
        mAttachedIndexBuffer->getNumberOfStoredIndices(), 
        mAttachedIndexBuffer->getDataType(), 
        0); 
    } 
    else 
    { 
    glDrawArrays(primMode, 0, mNumberOfStoredVertices); 
    } 

    if(mTexCoordBlockSize) 
    { 
    glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
    } 
    if(mColorBlockSize) 
    { 
    glDisableClientState(GL_COLOR_ARRAY); 
    } 
    if(mNormalBlockSize) 
    { 
    glDisableClientState(GL_NORMAL_ARRAY); 
    } 
    glDisableClientState(GL_VERTEX_ARRAY); 
}  
+1

На самом деле не отвечает на вопрос, можете ли вы хранить различные атрибуты в совершенно разных буферах, что вы определенно можете. –

+0

Мой плохой. Я просто не думал, что существует значительная разница между использованием суббуферов и полностью отдельными буферами для его цели, поскольку они будут распределены вместе, освобождены вместе и всегда будут использоваться вместе, если они содержат разные данные для одного и того же объекта. – PeterT

+0

Действительно, правда, но все же он задал вопрос, на который нужно ответить. –

2

Вы можете, конечно, иметь свои данные в разных буферах. Имейте в виду, что это вызов glVertexAttribPointer, который определяет источник атрибута. Поэтому, чтобы использовать другой буфер для атрибута, просто связывайте другой GL_ARRAY_BUFFER перед вызовом glVertexAttribPointer. glDrawElements не имеет ничего общего с ним:

glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 
glVertexAttribPointer(0, ...); 
glEnableVertexAttribArray(0); 
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glVertexAttribPointer(1, ...); 
glEnableVertexAttribArray(1); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 
glDrawElements(...); 

glDisableVertexAttribArray(0); 
glDisableVertexAttribArray(1); 

Если вы не используете РВО (который я не уверен, что это возможно в ES 2.0), просто назвать glVertexAttribPointer с другим массивом для каждого атрибута:

glVertexAttribPointer(0, ..., positionArray); 
glEnableVertexAttribArray(0); 
glVertexAttribPointer(1, ..., colorArray); 
glEnableVertexAttribArray(1); 

glDrawElements(..., indexArray); 

glDisableVertexAttribArray(0); 
glDisableVertexAttribArray(1); 

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

Но, конечно, вы можете использовать только один индексный массив для каждого объекта, а не разные массивы индексов для позиций и цветов. Это может потребовать, чтобы вы быстро обработали данные, считываемые из OBJ-файла, так как OBJ-файлы действительно могут использовать разные индексы для позиций, нормалей и texCoords.