Я изучаю 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();
}
Как только я увижу твой 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
один раз. Тогда вы увидите тот же результат.
Других решений пока нет …