五、OpenGL基礎變換與矩陣棧

1. 基礎變換

1.1 平移

平移

1.2 旋轉

旋轉

1.3 縮放

縮放

1.4 組合變換

平移和旋轉

先旋轉再平移
先平移再旋轉

對比上面2個變換,我們可以發(fā)現:在組合變換中,變換的順序是不可以隨意修改的。

  • 數學分析:這里分析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)
{
    //清除屏幕、深度緩存區(qū)
    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 優(yōu)化矩陣棧操作

不同的實現方式

之前我們用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();
        // 交換緩沖區(qū),并立即刷新
        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();
        // 交換緩沖區(qū),并立即刷新
        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();
        // 交換緩沖區(qū),并立即刷新
        glutSwapBuffers();
        glutPostRedisplay();
    }
    

2.1 矩陣棧過程

矩陣棧過程

上面就是矩陣棧的操作過程,其中:

  • 入棧,是為了保存當前的矩陣棧狀態(tài)
  • 出棧,是為了恢復入棧前的矩陣棧狀態(tài)

這個操作類似于iOSCore Graphiccontext的保存與恢復。

//保存
void CGContextSaveGState(CGContextRef c);
//恢復
void CGContextRestoreGState(CGContextRef c);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容