Современный OpenGL: стеки VBO, GLM и матрицы
после поиска и чтения о современном OpenGL, чтобы обновить мой существующий проект, я немного смущен, так как моя 3D-платформа основана на OpenGL 2.1.
Итак, насколько я знаю...
нам нужно генерировать наши объекты вершин-буферов из вершин, индексов, нормалей, цветов, uvs и т. д.
-
затем мы можем использовать GLM для преобразования матрицы, и мы используем только VBO для создания или управления сетками, наконец, мы передаем все в вершинный шейдер GLSL, как это...
glm::mat4 MVP = projection * view * model; glUniformMatrix4fv(glGetUniformLocation(shaderProgramID, "MVP"), 1, GL_FALSE, glm::value_ptr(MVP)); //or &MVP[0][0] // uniform mat4 MVP; // in vec3 Vertex; // void main(void) // { // gl_Position = MVP * vec4(Vertex, 1.0); //instead of ftransform(); // }
вопрос: как мы делаем иерархические преобразования без pushMatrix/popMatrix? (или, может быть, мы делаем иерархическое преобразование с помощью наших VBOs, это возможно?)
Если это невозможно, то как достичь того же результата, что и pushMatrix/popMatrix с помощью библиотеки GLM и C++ ?
допустим, мне нужно что-то вроде этого:
> Set identity
> Translate to X, Y, Z
> Draw Mesh 1
> Rotate 0.5 by X axis
> Draw Mesh 2
> Scale down to 0.1
> Draw Mesh 3
4 ответов
- нам нужно генерировать наши вершинные буферные объекты из вершин, индексов, нормалей, цветов, uvs и т. д.
на самом деле нет необходимости использовать VBOs, клиентские массивы вершин также работают. Однако настоятельно рекомендуется использовать VBO, потому что это облегчает жизнь водителя, а в долгосрочной перспективе и ваш, как тот, кто должен жонглировать данными. Накладные расходы кода пренебрегаются (это примерно то же самое, что и генерация и загрузка texture data) и производительность будет только увеличиваться.
- затем мы можем использовать GLM для преобразования матрицы, и мы используем только VBO для создания или управления сетками, наконец, мы передаем все в вершинный шейдер GLSL, как это...
вы не ограничены GLM. Подойдет любая матричная математическая библиотека. Если вы ищете что-то, что вы можете использовать в C99, посмотрите на мой (все еще неполный) linmath.h
https://github.com/datenwolf/linmath.h это просто заголовочный файл с static inline
функции. Мне еще предстоит проверить, оказывает ли дублирование кода негативное влияние на производительность (размер кода создает давление кэша L1).
вопрос: как мы делаем иерархические преобразования без pushMatrix / popMatrix? (или, может быть, мы делаем иерархическое преобразование с помощью наших VBOs, это возможно?)
VBOs не имеют ничего общего с этим. То, что дает большинству пользователей старомодных проблем OpenGL, - это те функции стека матрицы, которые делают OpenGL немного похожим на график сцены. Но это не так.
если вы забудете о матричном стеке старого OpenGL, станет очевидным, как делать иерархические транформации: в каждой ветви иерархии сделайте копию матрицы преобразования и действуйте на ней. Вы получаете иерархическое дерево преобразований, на каждом узле хранится соответствующая матрица. Затем вы проходите эти матрицы как униформы для вершинного шейдера; или только одна матрица, если вы рисуете жесткий объект, имеющий только одно преобразование. Несколько матриц обычно необходимо только для deformables как скелетная анимация персонажа такой
worldtransform ->
pelvis ->
left upper leg -> left lower leg -> left foot
right upper leg -> right lower leg -> right foot
torso ->
neck -> head ->
left eye
right eye
facial deformation // this is a whole chapter of it's own
left upper arm -> left lower arm -> left hand
right upper arm -> right lower arm -> right hand
каждый раз, когда вы enounter в ->
в такой иерархии вы делаете копию матрицы и продолжаете работать над ней. Когда вы возвращаетесь на более высокий уровень дерева, вы снова начинаете работать с этой матрицей.
да, если вам нужны иерархические преобразования, то вы должны сделать это самостоятельно. Однако это довольно тривиально, если все, что вам нужно, это стек. Просто возьмите последнюю запись в стеке и умножьте ее на следующую матрицу, которую вы хотите применить, и нажмите результат на стек.
[по вашему отредактированному вопросу]: вам вообще не нужен стек для этого, и нет никакого иерархического преобразования. Просто имейте одну матрицу и применяйте свой перевод, рисуйте, умножайте ее с помощью матрицы вращения нарисуйте, умножьте масштабирование, нарисуйте.
Если ваш рендеринг уже происходит иерархически, используя, например, рекурсию функций, то у вас уже есть стек матриц!
void renderMesh(Matrix transform, Mesh mesh)
{
// here call glDrawElements/glDrawArrays and send transform matrix to MVP uniform
mesh->draw(transform);
// now render all the sub-meshes, then will be transformed relative to current mesh
for (int i=0; i<mesh->subMeshCount(); i++)
{
Matrix subMeshTransform = mesh->getSubMeshTransform(i);
Mesh subMesh = mesh->getSubMesh();
renderMesh(subMeshTransform * transform, subMesh);
}
}
// somwhere in main function
...
Matrix projection = Matrix::perspective(...);
Matrix view = camera->getViewMatrix();
Matrix transform = view * projectIon;
renderMesh(transform, rootMesh);
в отношении производительности VAO и VBO я не согласен, что VBO быстрее, я предлагаю посмотреть эту ссылку
http://www.openglsuperbible.com/2013/12/09/vertex-array-performance/
вы можете видеть из результатов выше, что, по крайней мере, для нашего небольшого набора образцов, VAO быстрее во всех реализациях. Это разумно - есть меньше параметров для проверки при вызове glBindVertexArray, чем glBindBuffer или glVertexAttribPointer. Даже когда существует только один атрибут вершины, есть в два раза больше вызовов в OpenGL с коммутатором VAO, чем с явным обновлением глобального VAO. Помимо очевидного отношения "меньше вызовов API означает более быстрое выполнение", VAO-это место, где драйвер OpenGL может хранить информацию, необходимую для программирования базового GPU. Общее количество изменений состояния, отправленных на GPU, одинаково в любом случае.