Современный 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, одинаково в любом случае.