Передача VBO в шейдеры с разными раскладками - PullRequest
1 голос
/ 13 марта 2020

Если у меня есть вершинный шейдер, который ожидает этого ...

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec4 aBoneWeights; 
layout(location = 2) in vec4 aBoneIndices;

Как передать VBO, который уже организован для каждой вершины, как

Position(vec3) | Color(vec3) | UV(vec2) | BoneWeight(vec4) | BoneIndex(vec4)

Должен ли я сделать новый VBO? Если мои данные вершин чересстрочные, то нужно ли мне также создавать новый буфер данных вершин?

1 Ответ

1 голос
/ 13 марта 2020

Вариант 1. Создайте разные VAO для каждого шейдера

VAO определяет сопоставление ваших атрибутов шейдера (например, считывает vec3 из этого места памяти в VBO, с шагом N байтов, и сопоставьте его с атрибутом, связанным с местоположением X).

Некоторые глобальные для хранения имени VAO

GLuint g_vao;

Затем для его создания (для макета данных, который вы определили в своем шейдере) :

// create the VAO 
glCreateVertexArrays(1, &g_vao);


// set up: layout(location = 0) in vec3 aPos;
glEnableVertexArrayAttrib(g_vao, 0);  //< turn on attribute bound to location 0

// tell OpenGL that attribute 0 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  0,     //< the attribute index (location = 0)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  0,        //< the attribute index
  3,        //< there are 3 values xyz
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  0);       //< the offset (in bytes) from the start of the buffer where the data starts



// set up: layout(location = 1) in vec4 aBoneWeights
glEnableVertexArrayAttrib(g_vao, 1);  //< turn on attribute bound to location 0

// tell OpenGL that attribute 1 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  1,     //< the attribute index (location = 1)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  1,        //< the attribute index
  4,        //< there are 4 values
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  sizeof(float) * 8); //< the offset (in bytes) from the start of the buffer where the data starts



// set up: layout(location = 2) in vec4 aBoneIndices;
glEnableVertexArrayAttrib(g_vao, 2);  //< turn on attribute bound to location 2

// tell OpenGL that attribute 2 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  2,     //< the attribute index (location = 2)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  2,        //< the attribute index
  4,        //< there are 4 values xyz
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  sizeof(float) * 12);       //< the offset (in bytes) from the start of the buffer where the data starts

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

Я бы подумал, что вам нужны целые числа вместо чисел:

layout(location = 2) in ivec4 aBoneIndices;

Однако при связывании с целыми числами вам нужно использовать glVertexArrayAttribIFormat вместо glVertexArrayAttribFormat:

glVertexArrayAttribIFormat(
  g_vao,    //< the VAO
  2,        //< the attribute index
  4,        //< there are 4 indices
  GL_UNSIGNED_INT, //< all of type uint32
  sizeof(float) * 12);   

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

glVertexArrayVertexBuffer(
  g_vao, //< the VAO
  0,     //< the vertex buffer slot
  0,     //< offset (in bytes) into the buffer
  sizeof(float) * 16); //< num bytes between each element

Вариант 2: Просто используйте тот же VAO и тот же VBO

Просто закодируйте индексы, чтобы иметь определенные значения c, и тогда вы всегда сможете использовать один и тот же VAO.

layout(location = 0) in vec3 aPos;
//layout(location = 1) in vec4 aCol;  //< not used in this shader
//layout(location = 2) in vec4 aUv;   //< not used in this shader
layout(location = 3) in vec4 aBoneWeights; 
layout(location = 4) in vec4 aBoneIndices;

/ edit В ответ на ваш вопрос, это очень сильно зависит от версии OpenGL, которую вы используете. Ответ, который я разместил здесь, использует последние расширения прямого доступа к состоянию (DSA), найденные в OpenGL4.5. Если вы можете использовать их, я настоятельно рекомендую это сделать.

OpenGL 3.0: glVertexAttribPointer

Да, это будет работать. Однако этот механизм тесно связан с парадигмой OpenGL bind . Каждый атрибут эффективно связан с буфером, который был связан при вызове glVertexAttribPointer (т.е. вы будете делать: glBindBuffer (); glEnableVertexAttribArray (); glVertexAttribPointer ();) *) 1048 *.

Проблема с этим заключается в том, что он как бы блокирует вас для создания VAO для каждого VBO (или набора VBO, если извлекает данные из более чем одного) , поскольку атрибуты связаны с Точный буфер, который был связан, когда вы указали этот атрибут.

OpenGL 4.3: glVertexAttribFormat

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

Преимущество этого подхода по сравнению со старым API заключается в том, что привязать VAO к другому VBO (-ям) тривиально [то есть вы можете связать VAO с программой GLSL вместо того, чтобы иметь VAO для каждой пары VBO / Program] - просто вызов glBindVertexBuffer для каждого VBO, и он будет хорошо работать.

OpenGL 4.5: glVertexArrayAttribFormat

В долгосрочной перспективе это самая простая версия API для использования. Основным преимуществом является то, что вам не нужно беспокоиться о том, какой VAO в настоящее время связан (потому что вы передаете его в качестве аргумента) . Это имеет ряд преимуществ, потому что вам больше не важно, какой VAO был связан, и это также открывает двери для изменения объектов OpenGL из другого потока (чего не позволяли старые версии API).

...