Индексный буфер OpenGL не работает при загрузке объектов один за другим
Я изучаю OpenGL и столкнулся с "препятствием". Я нарисовал несколько домов (блоков и пирамид), используя индексный буфер. Это прекрасно работает при загрузке всех вершин (из всех домов) в вершинный буфер и использовании 1 большого индексного буфера. Теперь я хочу анимировать объекты и загружать их в вершинный буфер один за другим, чтобы я мог выполнять преобразование объектов один за другим. Код не сильно отличается от одного большого буфера, но когда я делаю это, я просто вижу, как на экране появляются случайные фигуры. Мой код выглядит следующим образом:
У меня есть мировой класс, который содержит список трехмерных объектов, имеет один большой список всех вершин (для ознакомительных целей), один большой список индексов (также пробный) и метод для добавления объекта Object3D в мир.
class World
{
public:
World();
~World();
vector<Object3D> objects;
vector<glm::vec3> Vertices;
vector<GLushort> Indices;
void AddObject(Object3D &object);
};
Класс Object3D:
class Object3D
{
public:
Object3D();
~Object3D();
glm::vec3 Position;
vector<glm::vec3> Vertices;
vector<unsigned int> Indices;
};
Метод World AddObject просто добавляет объект в список "объекты" и добавляет вершины и индексы в списки "Вершины" и "Индексы", чтобы создать один большой буфер:
void World::AddObject(Object3D &object) {
int oldVerticesSize = Vertices.size();
objects.push_back(object);
Vertices.insert(Vertices.end(), object.Vertices.begin(), object.Vertices.end());
for each (GLushort index in object.Indices)
{
Indices.push_back(index + oldVerticesSize);
}
}
Когда я рендерил большой буфер со всеми вершинами и индексами (как показано ниже), он работает нормально.
void WorldRenderer::Render()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(Vao); //use vao
glDrawElements(GL_TRIANGLES, World.Indices.size(), GL_UNSIGNED_SHORT, 0);
//glDrawArrays(GL_TRIANGLES, 0, World.Vertices.size());
glBindVertexArray(0); //release vao
//Model = glm::rotate(Model, 0.01f, glm::vec3(0.0f, 1.0f, 0.0f));
Mvp = Projection * View * Model;
glUniformMatrix4fv(UniformMvp, 1, GL_FALSE, glm::value_ptr(Mvp));
glutSwapBuffers();
//glutPostRedisplay();
}
Когда я зацикливаюсь на объектах и загружаю вершины объектов в буфер по одному объекту за раз (как показано ниже), он показывает некоторые случайные "формы", которые продолжают "стрелять вокруг" или быстро изменяться. Что я здесь не так делаю? Спасибо заранее за любые советы.
void WorldRenderer::Render()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int index = 0;
for each (Object3D mesh in World.objects)
{
Mvp = Projection * View * Model;
UpdateBuffer(mesh.Vertices, mesh.Indices);
glBindVertexArray(Vao); //use vao
glDrawElements(GL_TRIANGLES, mesh.Indices.size() , GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0); //release vao
glUniformMatrix4fv(UniformMvp, 1, GL_FALSE, glm::value_ptr(Mvp));
index++;
}
glutSwapBuffers();
}
Метод UpdateBuffers:
void WorldRenderer::UpdateBuffer(vector<glm::vec3> vertices, vector<unsigned int> indices) {
//fill Vbo
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//fill ibo
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(Vao);
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Ibo);
}
Для первого метода Render (без цикла) буферы создаются один раз в методе init, который выглядит следующим образом:
void WorldRenderer::Init(int argc, char ** argv) {
InitGlutGlew(argc, argv);
InitMatrices();
glDisable(GL_CULL_FACE);
//-------------- init buffers --------------
// vbo vertices
glGenBuffers(1, &Vbo);
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
glBufferData(GL_ARRAY_BUFFER, World.Vertices.size() * sizeof(glm::vec3),
&World.Vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//ibo
glGenBuffers(1, &Ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, World.Indices.size() * sizeof(unsigned int),
&World.Indices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// VAO setup
glGenVertexArrays(1, &Vao);
glBindVertexArray(Vao);
// Bind vertices to vao
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
//Bind elements
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Ibo);
//------ init shaders -----------
InitShader();
}
1 ответ
Как только я увижу твой UpdateBuffer
функция, я чувствую, что glBufferData
за vbo
не загружен должным образом.
Второй параметр vertices.size()
возвращает только количество элементов в векторе, но не фактический размер объекта.
Таким образом, второй параметр должен быть vertices.size() * sizeof(glm::vec3)
И третий параметр в порядке, так как он возвращает указатель на базовый массив. Если не работает.... прямо передайте адрес первого элемента, как показано ниже.
В целом это должно быть примерно так, как показано ниже.
glBufferData(
GL_ARRAY_BUFFER,
vertices.size() * sizeof(glm::vec3),
&vertices[0],
GL_STATIC_DRAW
);
Проверьте, работает ли это.
Почему вы видите различия?
Первый рендер:
Ваш буфер содержит все мировые данные для вершин непрерывно, когда glDrawElements
называется. Итак, здесь последняя вершина mesh1 продолжается с первой вершины mesh 2... Итак, вы видите своего рода замкнутую форму.
Второй рендер:
ваш буфер содержит только одну сетку данных за раз, когда glDrawElements
называется.
так что ваша форма заканчивается для каждой сетки после вызова glDrawElements
,
Чтобы получить тот же результат, что и при первом рендеринге, вы должны сначала обновить буфер одной вершины для всех сеток (используйте glBufferSubData
).
Тогда позвони glDrawElements
один раз. Тогда вы увидите тот же результат.