Как работает glDrawElementsBaseVertex?

У меня есть плоская плоскость и буфер индекса, или EBO с индексами, отмеченными на изображении:

введите описание изображения здесь

Теперь, если я позвоню:

glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, 0);

Я получаю это:

введите описание изображения здесь

Это я понимаю Далее, если я сделаю это:

glDrawElementsBaseVertex(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0, 0);

введите описание изображения здесь

Это тоже имеет смысл. Но мое понимание полностью разваливается, когда я изменяю один из других параметров. Если я сделаю это:

glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, 3);

Это выглядит так:

введите описание изображения здесь

Таким образом, передав аргумент 3 в аргумент basevertex (последний), он начал использовать индекс не с 3 позиций в индексе и даже не с 3 треугольников в индексе, а с 6 треугольниками или, точнее, индекс № 18. Я не могу понять это поведение.

Кроме того, я прочитал противоречивые значения аргумента "индексы" в этих функциях:

void glDrawElements(GLenum mode,  GLsizei count,  GLenum type,  const GLvoid * indices);
void glDrawElementsBaseVertex(GLenum mode​, GLsizei count​, GLenum type​, GLvoid *indices​, GLint basevertex​);

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

indices Указывает смещение байта (приведение к типу указателя) в буфер, связанный с GL_ELEMENT_ARRAY_BUFFER, с которого начинается чтение индексов.

А в другой версии написано:

Индексы Указывает указатель на место, где хранятся индексы.

Если я вызываю glDrawElementsBaseVertex со вторым последним аргументом (индексами) как (void*)3, я получаю первый треугольник, нарисованный красным. Если я укажу (void*)6, я не выделю треугольники. И если я укажу (void*)9, я выделю второй треугольник.

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

4 ответа

Если вы хотите иметь смещение для индексов, вы можете просто использовать glDrawElements следующим образом:

glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)(sizeof(GLuint)*6));

Он будет рисовать 3 элемента со смещением 6 для индексов.

Последним аргументом glDrawElements могут быть две разные вещи:

  1. если у вас нет буфера индексов, привязанного к GL_ELEMENT_ARRAY_BUFFER, это указатель на место, где хранятся индексы.
  2. если у вас есть буфер индексов, привязанный к GL_ELEMENT_ARRAY_BUFFER, это смещение в байтах для этого массива.

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

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

Точно так же, если вы хотите отобразить вторую половину ячейки 2, вы должны установить базовый указатель на 1, а указатель индекса на 3.

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

Я готов поспорить, что числа, которые вы включили в свое первое изображение, НЕ являются фактическими значениями индекса в вашем буфере индекса, а просто указывают ПОРЯДОК, в котором вы указываете вершины. Предположим, что ваш буфер вершин организован следующим образом:

где красные числа, которые я добавил, - это индексы массива буфера вершин, фактическое содержимое буфера индекса должно начинаться с {0,1,6,1,7,6,...}.

Итак, если basevertex равняется нулю, вызов glDrawElementsBaseVertex() выполняет следующие действия:

Когда i=0, нулевой элемент из вашего индексного буфера (который имеет нулевое значение) заставляет нулевой элемент (красные числа на приведенном выше изображении) в вашем буфере вершин использоваться как первая вершина вашего первого треугольника. При i=2 элемент два из буфера индекса (значение шесть) заставляет шестой элемент в буфере вершин завершить первый треугольник.

Цитата из документации для glDrawElementsBaseVertex()

"i-й элемент, переданный соответствующим вызовом отрисовки, будет взят из индексов элементов [i] + базовая вершина каждого включенного массива ".

Да, это немного сбивает с толку, но это означает, что после получения значения индекса из буфера индекса к нему добавляется базовая вершина. Итак, с basevertex равным 3, мы имеем следующее поведение:

Когда i=0, извлекается нулевой элемент из вашего индексного буфера. Как и раньше, оно имеет нулевое значение, но теперь к нему добавляется три. Таким образом, третий элемент в буфере вершин (опять же, красные числа на изображении выше) используется как первая вершина вашего первого треугольника. При i=2 извлекается элемент два из буфера индекса, который имеет значение шесть, но к нему добавляется три, поэтому элемент девять из буфера вершин используется для завершения первого треугольника.

Надеюсь это поможет.

glDrawElements

У нас есть позиции вершин.

      float positions[] =
{
    -0.5f, -1.0f, 0.0f,
    -1.5f,  1.0f, 0.0f,
    -2.5f, -1.0f, 0.0f,
     2.5f, -1.0f, 0.0f,
     1.5f,  1.0f, 0.0f,
     0.5f, -1.0f, 0.0f
};

У нас также есть индексы положения двух треугольных сеток.

      uint8 indices[] =
{
    0, 1, 2,
    3, 4, 5
};

Чтобы нарисовать эти две сетки, мы используем glDrawElementsкак показано ниже:

      // --------------------------------------------------------
// The first triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseIndex  = 0;
} mesh_1;

glDrawElements(
    /* mode   = */ GL_TRIANGLES,
    /* count  = */ mesh_1.numIndices,
    /* type   = */ GL_UNSIGNED_BYTE,
    /* offset = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ) );

// --------------------------------------------------------
// The second triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseIndex  = 3;
} mesh_2;

glDrawElements(
    /* mode   = */ GL_TRIANGLES,
    /* count  = */ mesh_2.numIndices,
    /* type   = */ GL_UNSIGNED_BYTE,
    /* offset = */ (void*)( sizeof( uint8 ) * mesh_2.baseIndex ) );

Обратите внимание, что следующие строки должны быть одного типа данных:

       uint8 indices[] =
/* type       = */ GL_UNSIGNED_BYTE,
/* offset     = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ) );

Тип данных должен быть одним из следующих: uint8, uint16или же uint32.

Результатом будет это изображение.

треугольники_1

glDrawElementsBaseVertex

Если мы хотим нарисовать таким образом несколько разных мешей, вероятно, каждый меш будет хранить свои индексы, начиная с нуля. Например, загрузив сцену с помощью библиотеки assimp , мы получим следующий набор индексов:

      uint8 indices[] =
{
    0, 1, 2,
    0, 1, 2
};

Давайте нарисуем и эти сетки.

      // --------------------------------------------------------
// The first triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseVertex = 0;
    int32 baseIndex  = 0;
} mesh_1;

glDrawElementsBaseVertex(
    /* mode       = */ GL_TRIANGLES,
    /* count      = */ mesh_1.numIndices,
    /* type       = */ GL_UNSIGNED_BYTE,
    /* offset     = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ),
    /* basevertex = */ mesh_1.baseVertex );

// --------------------------------------------------------
// The second triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseVertex = 3;
    int32 baseIndex  = 3;
} mesh_2;

glDrawElementsBaseVertex(
    /* mode       = */ GL_TRIANGLES,
    /* count      = */ mesh_2.numIndices,
    /* type       = */ GL_UNSIGNED_BYTE,
    /* offset     = */ (void*)( sizeof( uint8 ) * mesh_2.baseIndex ),
    /* basevertex = */ mesh_2.baseVertex );

В результате получится такое же изображение.

треугольники_2

Бонус

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

      int32 numVertices = 0;
int32 numIndices  = 0;

for ( auto& mesh : scene.meshes )
{
    mesh.numIndices = mesh.numTriangles * 3;

    mesh.baseVertex = numVertices;
    mesh.baseIndex  = numIndices;

    numVertices += mesh.numVertices;
    numIndices  += mesh.numIndices;
}

Например, при загрузке сцены «Низкополигональная сцена НЛО » результатом будет это изображение.

сцена

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