现代 OpenGL:VBO、GLM 和矩阵堆栈

Posted

技术标签:

【中文标题】现代 OpenGL:VBO、GLM 和矩阵堆栈【英文标题】:Modern OpenGL: VBO, GLM and Matrix Stacks 【发布时间】:2012-01-21 07:51:12 【问题描述】:

在搜索和阅读有关 Modern OpenGL 以升级我现有的项目后,我有点困惑,因为我的 3D 框架基于 OpenGL 2.1。

所以,据我所知...

我们需要从顶点、索引、法线、颜色、uvs 等生成我们的 Vertex-Buffer-Objects。

然后我们可以使用 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 的情况下进行分层转换? (或者也许我们通过使用我们的 VBO 来进行分层转换,这可能吗?)

如果不可能,那么如何使用 GLM 和 C++ 库实现与 pushMatrix/popMatrix 相同的结果?

假设我需要这样的东西:

> 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

【问题讨论】:

@BЈовић:OpenGL Mathematics (GLM),用于矩阵/向量数学的仅标头 C++ 库。 【参考方案1】:

如果您的渲染已经使用例如函数递归进行分层,那么您已经有了矩阵堆栈!

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);

【讨论】:

【参考方案2】:
我们需要从顶点、索引、法线、颜色、uvs 等生成我们的 Vertex-Buffer-Objects。

实际上没有必要使用 VBO,客户端顶点数组也可以。但是强烈建议使用 VBO,因为它使驱动程序的生活更轻松,从长远来看,您也可以作为必须处理数据的人。代码开销可以忽略不计(与生成和上传纹理数据差不多),性能只会提高。

然后我们可以使用 GLM 进行矩阵变换,我们只使用 VBO 来创建或操作网格,最后我们像这样将所有内容传递给 GLSL 顶点着色器...

您不仅限于 GLM。任何矩阵数学库都可以。如果您正在寻找可以在 C99 中使用的东西,请查看我的(仍然不完整)linmath.hhttps://github.com/datenwolf/linmath.h,它只是一个带有 static inline 函数的头文件。如果代码重复对性能有负面影响(代码大小会造成 L1 缓存压力),我还没有进行基准测试。

问题:我们如何在没有 pushMatrix/popMatrix 的情况下进行分层转换? (或者也许我们通过使用我们的 VBO 来进行分层转换,这可能吗?)

VBO 与此无关。给大多数老式 OpenGL 用户带来麻烦的是那些矩阵堆栈函数,它们使 OpenGL 看起来有点像场景图。但事实并非如此。

如果您忘记了旧 OpenGL 的矩阵堆栈,那么如何进行层次转换就变得很明显了:在层次结构中的每个分支处复制转换矩阵并对其进行操作。您会得到一个层次结构的转换树,在每个节点上存储相应的矩阵。然后将这些矩阵作为制服传递给顶点着色器;或者如果您正在绘制一个只有一个变换的刚性对象,则只有一个矩阵。多个矩阵,你通常只需要像这样的角色的骨骼动画这样的可变形对象

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

每次您在这样的层次结构中遇到-&gt; 时,您都会复制矩阵并继续处理该矩阵。当回退到树的更高级别时,您会再次从该矩阵开始工作。

【讨论】:

"面部变形" //这是它自己的一整章 => 包括下巴:)【参考方案3】:

是的,如果您需要分层转换,那么您必须自己完成。但是,如果您只需要一个堆栈,那么这几乎是微不足道的。只需将堆栈中的最后一个条目与您要应用的下一个矩阵相乘,然后将结果压入堆栈。

[关于您编辑的问题]:您根本不需要堆栈,也没有分层转换。只需一个矩阵并应用您的平移、绘制、将其与旋转矩阵相乘、绘制、乘以缩放、绘制。

【讨论】:

呃?如果你有初始矩阵 M、平移 T、旋转 R 和缩放 S,那么你的第一个矩阵是 M=T,第二个矩阵是 M*=R = (TR == 先平移,旋转第二个),第三个是 M*=S = (TR*S == 所有矩阵。) 对于机械臂,您也可以从顶部开始并在向下移动时发出绘图调用树。 “堆栈”是 std::stack 还是递归场景遍历例程的一部分都没有关系。 如果你不做 M*=S,而只是使用 MS(复制!),那么原始矩阵不会改变。你的堆栈看起来像这样:[M],然后你压入 T(导致堆栈 [M,MT]),然后你弹出([M]),然后再次压入 R([M,M *R])。我不明白你的问题? 停止编辑您的 cmets 的含义。我说你可以通过堆栈来实现它,或者你可以在递归绘图函数中复制你的矩阵(隐式创建一个堆栈。) [M, ..] 是一个堆栈,如果你想要这个,就用它。但是你也可以在没有任何 std::stack 的情况下轻松逃脱。 让我们continue this discussion in chat【参考方案4】:

关于 VAO 和 VBO 性能我不同意 VBO 更快,我建议查看此链接

http://www.openglsuperbible.com/2013/12/09/vertex-array-performance/

从上面的结果可以看出,至少对于我们的小样本集,VAO 在所有实现上都更快。这是有道理的——调用 glBindVertexArray 时验证的参数少于 glBindBuffer 或 glVertexAttribPointer。即使只有一个顶点属性,使用 VAO 开关调用 OpenGL 的次数也比使用全局 VAO 显式更新的次数少一半。除了明显的“更少的 API 调用意味着更快的执行”关系之外,VAO 是 OpenGL 驱动程序可以存储编程底层 GPU 所需信息的地方。无论哪种方式,发送到 GPU 的状态更改总量都是相同的。

【讨论】:

这与问题有什么关系?

以上是关于现代 OpenGL:VBO、GLM 和矩阵堆栈的主要内容,如果未能解决你的问题,请参考以下文章

为 3D Cup 创建圆柱体基础(现代 OpenGL、GLM)

OpenGL转换不使用GLM矩阵

opengl旋转的对象不居中

旋转矩阵的问题(openGL/glm)[关闭]

OpenGL 数学库 GLM 投影矩阵公式 glm::perspective 已经抛弃角度的使用,转为使用弧度

使用 OpenGL 3.3 进行实例化似乎很慢