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