【问题标题】:Replicate sequence of GL matrix ops manually手动复制 GL 矩阵操作序列
【发布时间】:2012-02-06 03:36:03
【问题描述】:

我正在将玩具 GL ES 1.1 代码库移植到 GL ES 2.0。没有内置的模型视图矩阵操作,所以我尝试用 4x4“当前矩阵”上的操作替换 glScalefglTranslatefglRotatef 调用。

但是,我的矩阵数学有点粗略,并且乘法排序和行与列主要符号等发生了一些事情,如果有人能快速概述我应该在这里介绍的语义,我会很高兴。

例如,假设我从一个单位矩阵开始,然后(在 ES 1.1 代码中)按顺序执行:

glTranslate(A);
glRotate(B);
glTranslate(C);

...我需要对我的“当前矩阵”进行什么数学运算才能在我发送到着色器的最终模型视图矩阵中复制此功能?我尝试保留一个矩阵,并为每个操作创建一个表示它的新矩阵,并将其乘以当前的矩阵。虽然结果不正确,但我认为我对前/后乘法、-major 等有些东西不了解。

有人愿意在这里就理论说几句话吗?谢谢。 (在让它变得超级高效之前,我想了解基础知识。)

(这个:Implement the Fixed function Pipeline efficent in OpenGL ES 2.0? 问了一个类似的问题,但答案是关于如何提高效率,并没有真正启发我数学等价性。)

【问题讨论】:

  • 只要找到 OpenGL 规范。每个操作都有所有的矩阵公式。

标签: opengl-es linear-algebra


【解决方案1】:

基本上,您正在寻找替代 opengl 矩阵堆栈来进行转换。

假设您使用的是 c/c++,我建议您看看 GLM 库:http://glm.g-truc.net/

这是一个仅包含标头的库,易于使用,是 opengl es 2.0 之前的矩阵堆栈的完美替代品。它甚至为已弃用的 glu 函数(如 gluOrtho() 和 gluPerspective())提供功能。 Glm 的矩阵可以很容易地传递给您的着色器,因为它们是在考虑着色器的情况下构思的。

您必须对代码进行一些更改;例如将您自己的顶点/矩阵定义转换为 glm::vec3 / glm::mat4。

下面是我如何构造传递给顶点着色器的 mvp 矩阵的示例:在这种情况下,actorInstance 类具有一些属性,例如位置(在世界中)和定义为 glm::vec3 的旋转。构造的模型、模型/视图和模型/视图/投影矩阵都是类的属性:

void CActorInstance::update(glm::mat4 viewMatrix, glm::mat4 projectionMatrix)
{

    // act according to class behavior
    this->actorClass->act(&input, &world, &direction, &rotation);

    // calculate the translation matrix
    glm::mat4 translate = glm::mat4();
    translate = glm::translate( glm::mat4(), world);

    // calculate the rotation matrix   
    glm::mat4 rotateX = glm::rotate( glm::mat4(1.0f), rotation.x, glm::vec3(1,0,0));
    glm::mat4 rotateY = glm::rotate( glm::mat4(1.0f), rotation.y, glm::vec3(0,1,0));
    glm::mat4 rotateZ = glm::rotate( glm::mat4(1.0f), rotation.z, glm::vec3(0,0,1));
    glm::mat4 rotate = rotateX * rotateY * rotateZ;

    // calculate the model matrix
    mMatrix = translate * rotate;

    // calculate the model/view matrix
    mvMatrix = viewMatrix * mMatrix;

    // calculate the model/view/projection matrix
    mvpMatrix = projectionMatrix * mvMatrix;
};

显然,每个对象的 mvp 矩阵都会根据对象的位置和旋转在每一帧中更新。 viewMatrix 和 projectionMatrix 是从我的相机类传下来的。 然后使用该矩阵来渲染网格:

    void CMesh::renderMesh(GLuint program, glm::mat4 *mvp)
    {
        glUseProgram(program);

        int mvpLocation = glGetUniformLocation(program, "mvpMatrix");
        int texLocation = glGetUniformLocation(program, "baseMap");
        glUniformMatrix4fv( mvpLocation, 1, GL_FALSE, glm::value_ptr(*mvp));

// rendering code ommitted


    };

希望这会有所帮助:)

编辑:实现矩阵堆栈的建议

stl::stack<glm::mat4> matrixStack; // the matrix stack
matrixStack.push_back(glm::mat4()); // push an identity matrix on the stack

这就是你的 glLoadIdentity()...

【讨论】:

  • 谢谢埃里克。这里的含义是您正在批量处理您正在批量处理所有平移、缩放和旋转更改,并在渲染之前将它们全部作为一个矩阵构建进行。如果我一次将它们放入“当前矩阵”中,我试图找出行为。如果我的问题不明智,那么请帮助推动我理解?谢谢。
  • 你所说的“批处理”到底是什么意思?即使使用 pre-opengles 2 矩阵堆栈,如果您想转换多个对象,您也必须在某个时候调用 glPushMatrix() 和 glPopMatrix()。就个人而言,我不明白您为什么要复制以前的堆栈行为……很难跟踪当前矩阵的外观。话虽如此,没有什么能阻止您将堆叠矩阵与 glm 一起使用。
  • 感谢您提供进一步的见解和更新。我真正要问的是,如果我的操作是:按顺序翻译、旋转、翻译,这会执行一些不同于预先知道我的所有旋转和翻译的操作序列。保持堆栈并不困难,它了解我做什么来获得翻译,然后旋转,然后从我的乘法中翻译行为。这有帮助吗?
  • 我不确定你的意思,但没有区别;只需将矩阵右乘(正如 Christian 在他的回答中提到的那样),结果应该是正确的。只是,我会分两步完成:M' = t1M x rM; M'' = t2M x M';
  • 谢谢。您能解释一下您使用的符号(我不知道 t1-、t2- 和 r- 前缀是什么意思)以及为什么要分两步执行此操作吗?
【解决方案2】:

我尝试保留一个矩阵,并为每个操作创建一个新的 表示它的矩阵,并将其与当前矩阵相乘。

嗯,就是这样。没什么好看的。

分别调用glRotateglTranslateglScale(当然还有glOrthoglFrustumgluLookAtgluPerspective)的矩阵都相当容易构建,如图所示在链接中。

你只需要在当前矩阵M上右乘相应的变换矩阵T

M' = M * T

因为我们(或您要复制的旧 OpenGL)希望在绘制之前调用的最后一个转换首先应用于对象。

那么你还必须与你的矩阵的存储保持一致(行优先与列优先),无论你解决哪个问题。但是由于您使用的是 OpenGL,因此以列为主的存储将是一个好主意,因为它可以让您更轻松地将它们上传到 GLSL(尽管没有反对转置标志),与矢量化 SIMD 指令(如 x86 的 SSE)一起使用效果更好此外,与为 OpenGL 设计的其他库(出于同样的原因使用列主矩阵)配合使用效果更好。只需与存储保持一致,不要在不同的函数调用中混淆。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    相关资源
    最近更新 更多