美文网首页OpenGL
五、OpenGL基础变换与矩阵栈

五、OpenGL基础变换与矩阵栈

作者: 收纳箱 | 来源:发表于2020-02-23 22:27 被阅读0次

    1. 基础变换

    1.1 平移

    平移

    1.2 旋转

    旋转

    1.3 缩放

    缩放

    1.4 组合变换

    平移和旋转

    先旋转再平移 先平移再旋转

    对比上面2个变换,我们可以发现:在组合变换中,变换的顺序是不可以随意修改的。

    • 数学分析:这里分析2D变换。
    //先旋转再平移
                         ┏ cosθ   sinθ  0 ┓┏ 1    0    0 ┓
    [X, Y, 1] = [x, y, 1]┃ -sinθ  cosθ  0 ┃┃ 0    1    0 ┃
                         ┗ 0      0     1 ┛┗ dx   dy   1 ┛
    
    //先平移再旋转
                         ┏ 1    0    0 ┓┏ cosθ   sinθ  0 ┓
    [X, Y, 1] = [x, y, 1]┃ 0    1    0 ┃┃ -sinθ  cosθ  0 ┃
                         ┗ dx   dy   1 ┛┗ 0      0     1 ┛
    

    这个的问题本质原因是矩阵的乘法不满足交换律

    1.5 代码示例

    效果
    • 先移动再旋转
    GLfloat xPos = 0.0f;
    GLfloat yPos = 0.0f;
    //红色
    GLfloat vRed[] = {1.0,0.0,0,1};
    
    void RenderScene(void)
    {
        //清除屏幕、深度缓存区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //1.建立基于时间变化的动画
        static CStopWatch rotTimer;
        //当前时间 * 60s
        float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
        
        //2.矩阵变量
        /*
         mView: 平移
         mModel: 旋转
         mModelView: 模型视图
         mModelViewProjection: 模型视图投影MVP
         */
        M3DMatrix44f mView, mModel, mModelView, mModelViewProjection;
    
        //mModel旋转矩阵,绕y轴旋转yRot度
        m3dRotationMatrix44(mModel, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
    
        //mView平移矩阵,沿z轴移动-2.5
        m3dTranslationMatrix44(mView, 0.0f, 0.0f, -2.5f);
        
        //mModelview = mView * mModel
        m3dMatrixMultiply44(mModelview, mView, mModel);
        
        //mModelViewProjection = ProjectionMatrix * mView * mModel
         m3dMatrixMultiply44(mModelViewProjection,
                             viewFrustum.GetProjectionMatrix(),
                             mModelview);
      
        GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
        shaderManager.UseStockShader(GLT_SHADER_FLAT, 
                                     mModelViewProjection, 
                                     vBlack);
        torusBatch.Draw();
    
        glutSwapBuffers();
        glutPostRedisplay();
    }
    

    2. 矩阵栈

    我们在矩阵储存时可能用到以下实例:

    GLMatrixStack       modelViewMatrix;
    GLMatrixStack       projectionMatrix;
    GLFrame             cameraFrame;
    GLFrame             objectFrame;
    

    其中,cameraFrame用于存储观察者矩阵,objectFrame用于存储模型矩阵。projectionMatrix只用于存储投影矩阵,我们操作最多的是modelViewMatrix

    • 模型矩阵绕世界坐标系y轴旋转-5.0度
    objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    
    • 观察者矩阵向后退15.0
    //GLFrame中默认的朝向是z轴的负方向,即(0.0, 0.0, -1.0)
    //向前走-15.0,即(0.0, 0.0, -1.0 * -15.0) = (0.0, 0.0, 15.0)
    cameraFrame.MoveForward(-15.0f);
    
    • 渲染过程中矩阵栈操作,获取MVP矩阵的计算结果
    //压栈
    modelViewMatrix.PushMatrix();
    //获取观察者矩阵
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    //栈顶矩阵乘以传入矩阵,相乘的结果简存储在栈顶
    modelViewMatrix.MultMatrix(mCamera);
    //获取模型矩阵
    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);
    //由于先乘的观察者矩阵,现在再乘以模型矩阵
    //栈顶 = M_view * M_model
    modelViewMatrix.MultMatrix(mObjectFrame);
    ...
    //由于初始化时传入了,modelViewMatrix和projectionMatrix的引用
    //transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //下面的代码会计算projectionMatrix * modelViewMatrix
    //结合上面的代码,等价于M_projection * M_view * M_model
    transformPipeline.GetModelViewProjectionMatrix()
    ...
    //出栈
    modelViewMatrix.PopMatrix();
    

    2.1 优化矩阵栈操作

    不同的实现方式

    之前我们用cameraFrameobjectFrame来记录相机和模型的变化,用到了两个对象。但我们可以固定一个对象,变化另一个对象。

    例如,之前的做法需要观察者向后退,同时物体旋转,把变化作用到了两个物体上,所以用到了两个矩阵分别记录两个物体的变化,最后再使用矩阵相乘把两个变化合并起来。

    如果固定一个物体,那么根据相对运动,就是把之前两个物体的变化,相对地作用到另一个物体上,就可以少用一个矩阵了。

    下面我们看看代码如何实现:

    • 目前的做法

      void SetupRC() {
          ...
          // 相机向z轴正方向移动2.5
          cameraFrame.MoveForward(-2.5f);
      }
      void RenderScene(void) {
          ...
          // 模型每次刷新时多转-1.0度  
          objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f);
          // 入栈      
          modelViewMatrix.PushMatrix();
          // 获取观察者矩阵
          M3DMatrix44f mCamera;
          cameraFrame.GetCameraMatrix(mCamera);
          modelViewMatrix.MultMatrix(mCamera);
          // 获取模型矩阵
          M3DMatrix44f mModelview;
          objectFrame.GetMatrix(mModelview);
          modelViewMatrix.MultMatrix(mModelview);
          ...
          // 出栈
          modelViewMatrix.PopMatrix();
          // 交换缓冲区,并立即刷新
          glutSwapBuffers();
          glutPostRedisplay();
      }
      
    • 只使用objectFrame

      void SetupRC() {
          ...
          // 相对运动
          // 模型向z轴负方向移动2.5
          objectFrame.MoveForward(2.5f);
      }
      void RenderScene(void) {
          ...
          // 模型每次刷新时多转-1.0度  
          objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f);
          // 入栈      
          modelViewMatrix.PushMatrix();
          // 获取模型矩阵
          M3DMatrix44f mModelview;
          objectFrame.GetMatrix(mModelview);
          modelViewMatrix.MultMatrix(mModelview);
          ...
          // 出栈
          modelViewMatrix.PopMatrix();
          // 交换缓冲区,并立即刷新
          glutSwapBuffers();
          glutPostRedisplay();
      }
      

      又因为GLMatrixStackPushMatrix(GLFrame& frame) 方法,可以一次解决多行代码:

      void PushMatrix(GLFrame& frame) {
          M3DMatrix44f m;
          frame.GetMatrix(m);
          PushMatrix(m);
      }
      

      使用上面的API可以让代码更简洁:

      void SetupRC() {
          ...
          // 相对运动
          // 模型向z轴负方向移动2.5
          objectFrame.MoveForward(2.5f);
      }
      void RenderScene(void) {
          ...
          // 模型每次刷新时多转-1.0度  
          objectFrame.RotateWorld(m3dDegToRad(-1.0), 0.0f, 1.0f, 0.0f);
          // 入栈模型矩阵  
          modelViewMatrix.MultMatrix(objectFrame);
          ...
          // 出栈
          modelViewMatrix.PopMatrix();
          // 交换缓冲区,并立即刷新
          glutSwapBuffers();
          glutPostRedisplay();
      }
      

    2.1 矩阵栈过程

    矩阵栈过程

    上面就是矩阵栈的操作过程,其中:

    • 入栈,是为了保存当前的矩阵栈状态
    • 出栈,是为了恢复入栈前的矩阵栈状态

    这个操作类似于iOSCore Graphiccontext的保存与恢复。

    //保存
    void CGContextSaveGState(CGContextRef c);
    //恢复
    void CGContextRestoreGState(CGContextRef c);
    

    相关文章

      网友评论

        本文标题:五、OpenGL基础变换与矩阵栈

        本文链接:https://www.haomeiwen.com/subject/ppzsqhtx.html