VAO и VBO для рендеринга различных объектов

Я написал этот класс «Model» для загрузки файлов .obj и распределения данных для них в VBO.
Его код выглядит примерно так: (обратите внимание, что он не использует VAO)

class Model {...}

void Model::LoadOBJ(const char *file)
{
//load vertices, normals, UVs, and put them all in _vec, which is a private data member of std::vector<glm::vec3>
...

//if an .obj file is loaded for the first time, generate a buffer object and bind it
if(glIsBuffer(_vbo) == GL_FALSE)
{
glGenBuffers(1, &_vbo);//_vbo is a private data member
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
}

//load the data in the array buffer object
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _vec.size(), &_vec[0][0], GL_STATIC_DRAW);
}

void Model::Draw()
{
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glDrawArrays(GL_TRIANGLES, 0, _numVertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}

Раньше я думал, что следующий код будет хорошо работать для рендеринга двух разных объектов:

void init()
{
//vao dummy (?)
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

//load 3d models
Model cube = load("cube.obj");
Model cow = load("cow.obj");

//the next two lines should be valid for both objects?
glVertexAttribPointer(prog.GetAttribLocation("vertex"), 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(prog.GetAttribLocation("vertex"));
}

void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//draw cube:
//some matrix transformations
...
cube.Draw();

//draw cow:
//some matrix transformations
...
cow.Draw()

glutSwapBuffers();
}

но оказывается, что OpenGL просто нарисует две коровы или два куба. (зависит от того, какую модель я загружаю последней в init ())
две коровы
два куба

Кстати, я почти уверен, что на первом изображении opengl попытался нарисовать двух коров, но функция glDrawArrays () была вызвана с количеством вершин, необходимых для куба.

Так чего мне не хватает? Нужно ли другое VAO для каждого объекта буфера или что-то подобное?

4

Решение

Здесь виновато «текущее» состояние, особенно в отношении glVertexAttribPointer,

Все звонки на glVertexAttribPointer (...) установить указатели памяти относительно объекта буфера В настоящее время связан с GL_ARRAY_BUFFER, Мы склонны называть объект буфера, который связан с этим местоположением Объект буфера вершины, но в действительности один объект буфера может использоваться для нескольких целей, и у них фактически нет типа.

Подумайте об этом вроде как в современном OpenGL:

GLuint GL_ARRAY_BUFFER_BINDING = 0; // Only 1 or 2 commands in GL care about this state
GLuint GL_VERTEX_ARRAY_BINDING = 0; // 0 is an invalid VAO (if this is 0, most vertex commands will generate `GL_INVALID_OPERATION`).

// Generic GPU-side Memory Store
struct GLBufferObject {
GLsizeiptr* gpu_base_addr;
} *gl_buffer_objects;

// Vertex Array State
struct GLVertexArrayObject {
GLsizeiptr* attribute_pointers [GL_MAX_VERTEX_ATTRIBUTES];
GLboolean   attribute_enabled  [GL_MAX_VERTEX_ATTRIBUTES];
GLuint      GL_ELEMENT_ARRAY_BUFFER_BINDING;
} *gl_array_objects;

void glBindVertexArray (GLuint array)
{
GL_VERTEX_ARRAY_BINDING = array;
}

void glBindBuffer (GLenum target, GLuint buffer)
{
if (target == GL_ARRAY_BUFFER)
GL_ARRAY_BUFFER_BINDING = buffer;
}

void glVertexAttribPointer (GLuint index, ..., const GLvoid* offset)
{
GLBufferObject*      current_vbo = &gl_buffer_objects [GL_ARRAY_BUFFER_BINDING];
GLVertexArrayObject* current_vao = &gl_array_objects  [GL_VERTEX_ARRAY_BINDING];

current_vao->attribute_pointers [index] = current_vbo->gpu_base_addr + offset;
}

Смысл этого псевдокода в том, чтобы показать вам, что в OpenGL есть только одна команда, к которой вы привязали GL_ARRAY_BUFFER имеет значение во всем вашем коде: glVertexAttribPointer (...),

Все остальные команды, такие как glDrawArrays (...) на самом деле использовать состояние, хранящееся вcurrent_vao«Как показано в макете реализации glVertexAttribPointer (...),


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

Обзор, в котором говорится GLVetexArrayObject хранит в псевдокоде, а затем рассмотрите возможность рефакторинга вашего собственного кода, чтобы воспользоваться этим. В противном случае вам придется сделать хотя бы один звонок glVertexAttribPointer каждый раз, когда вы звоните void Model::Draw(),

Для менее интересного объяснения, см. Это связанный ответ.

4

Другие решения

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

Если вы используете разные vaos для разных моделей, данные атрибутов будут храниться в vao, и вам не нужно будет повторно привязывать атрибуты (просто меняйте vao) каждый раз, когда вы рисуете новый объект.

2

По вопросам рекламы [email protected]