OpenGLES 2.0 отдельные буферы для вершин, цветов и координат текстуры

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

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

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 parser в obj-c и мне было интересно: что делать, если вы загружаете модель без текстуры и хотите только показать цвет на объекте? Или: что делать, если вы хотите загрузить модель только с текстура сопоставлена с ней, но нет отдельного цвета на вершину? И: не помещает значения цвета и координаты текстуры, раздувая структуру вершин с слишком большим количеством данных.

3 ответов


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

последний раз, когда я контактировал с 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);

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

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

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, я бы предложил Нэхэ учебники.


вы конечно можете иметь ваши данные в разных буферах. Имейте в виду, что это 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);

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

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

glDrawElements(..., indexArray);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);

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

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


вы можете разделить их на суб-буферы, но если вы используете их, то вы должны иметь их для всех вершин, и если вы используете индексные буферы, то вы должны использовать один индексный буфер для всех (позиция, цвет, 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);
}