OpenGLES 2.0 отдельные буферы для вершин, цветов и текстурных координат - PullRequest
10 голосов
/ 06 января 2012

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

Ответы [ 3 ]

21 голосов
/ 06 января 2012

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

Последний раз, когда я вступал в контакт с ES 2.0, был в контексте WebGL (которыйis имеет немного иную спецификацию, но в конечном итоге основана на 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 руководства .

2 голосов
/ 06 января 2012

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

Если VBO не используются (что, я не уверен, возможно в 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.

1 голос
/ 06 января 2012

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

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

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);

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

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);
}    
...