OpenGL矩陣和矩陣堆棧與基礎(chǔ)變換的理解和使用

  • 矩陣
    矩陣是一種功能非常強(qiáng)大的數(shù)學(xué)工具,它大大簡化了了求解變量之間有復(fù)雜關(guān)系的方程式或者方程組的過程。矩陣在坐標(biāo)變換中運(yùn)用得非常廣泛。例如:如果在空間有一個點,由x,y和z坐標(biāo)定義,將它圍繞任意點沿任意方向旋轉(zhuǎn)一定的角度后,我們需要知道這個點現(xiàn)在的位置,就要用到矩陣。因為新的x坐標(biāo)不僅與原來的x坐標(biāo)和其他旋轉(zhuǎn)參數(shù)有關(guān),還與y和z坐標(biāo)值有關(guān)。這種變量與解之間的相關(guān)性就是矩陣最擅長解決的問題。

  • 基礎(chǔ)變換
    由于我們的屏幕是2D的,在把3D圖形數(shù)據(jù)顯示在2D屏幕上的這個過程中,我們需要對頂點數(shù)據(jù)進(jìn)行幾何變換:視圖變換、模型變換和投影變換。

    變 換 應(yīng) ?
    視圖 指定觀察者位置
    模型 在場景中移動物體
    模型視圖 描述視圖/模型變換的?二元性
    投影 改變視景體?大?小和設(shè)置它的投影?方式
    視口 偽變化,對窗?口上最終輸出進(jìn)?行行縮放
  • 模型視圖矩陣
    模型視圖矩陣是一個4x4矩陣,它表示一個變換后的坐標(biāo)系,我們可以用來放置對象和確定對象的方向。我們?yōu)閳D元提供的頂點將作為一個單列矩陣的形式來使用,并乘以一個模型視圖矩陣來獲得一個相對于視覺坐標(biāo)系的經(jīng)過變換的新坐標(biāo)。

    • 我們可以調(diào)用math3d庫中m3dTranslationMatrix44函數(shù)來使用變換矩陣
      void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)
      
    • 旋轉(zhuǎn)
      void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
      
    • 縮放
      void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale)
      
    • 綜合變換:將兩個矩陣相乘并返回結(jié)果
      void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
      

    提示:如果不知道參數(shù)是什么意思,可以進(jìn)入看源碼,非常的清晰。

  • 投影矩陣
    投影分為正投影和透視投影。

    • 正投影會產(chǎn)生一個平行投影,這種投影在繪制從遠(yuǎn)處觀察不產(chǎn)生任何透視縮短的特定物體時非常有用。
      // 用這個函數(shù)設(shè)置正投影
      GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax,   GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax);
      
    • 透視投影會有一個3D效果,看起來更加逼真,渲染3D圖形時常用透視投影。
      // 用這個函數(shù)設(shè)置透視投影
      GLFrustum::SetPerspective(float fFov, float fAspect, float fNear, float fFar);
      
  • 頂點變換管線

    原始頂點數(shù)據(jù)到顯示在窗口上的變化過程,這個過程都是用矩陣記錄每一次放射變換,然后應(yīng)用到頂點數(shù)據(jù)上。
    屏幕快照 2019-05-21 22.43.51.png
  • 使用矩陣?yán)?/li>
// GLTool.h頭文件包含了大部分GLTool中類似C語言的獨(dú)立函數(shù)
#include "GLTools.h"
// 矩陣?工具類,?用來快速設(shè)置正/透視投影
#include "GLFrustum.h"
// 三?角形批次類,幫助類,利利?用它可以傳輸頂點/光照/紋理理/顏?色數(shù)據(jù)到存儲著?色器?中
#include "GLBatch.h"
#include "StopWatch.h"
// 數(shù)學(xué)庫
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

// 設(shè)置圖元繪制時的投影方式
GLFrustum           viewFrustum;
//  存儲著?色器?管理理?工具類.
GLShaderManager     shaderManager;
GLTriangleBatch     torusBatch;


// 設(shè)置視口和投影矩陣
void ChangeSize(int w, int h)
{
    //防止除以零
    if(h == 0)
        h = 1;
    
    //將視口設(shè)置為窗口尺寸
    glViewport(0, 0, w, h);
    
    //設(shè)置透視投影
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 1000.0f);
}

//召喚場景
void RenderScene(void)
{
    //清除屏幕、深度緩存區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //1.建立基于時間變化的動畫
    static CStopWatch rotTimer;
    //當(dāng)前時間 * 60s
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //2.矩陣變量
    /*
     mTranslate: 平移
     mRotate: 旋轉(zhuǎn)
     mModelview: 模型視圖
     mModelViewProjection: 模型視圖投影MVP
     */
    M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
    
    //創(chuàng)建一個4*4矩陣變量,將花托沿著Z軸負(fù)方向移動2.5個單位長度
    m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
    
    //創(chuàng)建一個4*4矩陣變量,將花托在Y軸上渲染yRot度,yRot根據(jù)經(jīng)過時間設(shè)置動畫幀率
     m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
    
    //為mModerView 通過矩陣旋轉(zhuǎn)矩陣、移動矩陣相乘,將結(jié)果添加到mModerView上
    m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
    
    // 將投影矩陣乘以模型視圖矩陣,將變化結(jié)果通過矩陣乘法應(yīng)用到mModelViewProjection矩陣上
    //注意順序: 投影 * 模型 != 模型 * 投影
     m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
  
    //繪圖顏色
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    
    //通過平面著色器提交矩陣,和顏色。
    shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
    //開始繪圖
    torusBatch.Draw();
    
    // 交換緩沖區(qū),并立即刷新
    glutSwapBuffers();
    glutPostRedisplay();
}

void SetupRC()
{
    //1.
    glClearColor(0.8f, 0.8f, 0.8f, 1.0f );
    shaderManager.InitializeStockShaders();
    glEnable(GL_DEPTH_TEST);
   
    //2.形成一個球
    gltMakeSphere(torusBatch, 0.4f, 10, 20);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("ModelViewProjection Example");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    
    glutMainLoop();
    return 0;
}
  • 矩陣堆棧介紹
    1. 矩陣堆棧常用函數(shù)

    //這個類的構(gòu)造函數(shù)允許指定堆棧的最大深度,默認(rèn)的堆棧深度為64,這個矩陣堆棧在初始化時已經(jīng)在堆棧中包含了單元矩陣。
    GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
    //在堆棧頂部載?一個單元矩陣
    void GLMatrixStack::LoadIdentity(void);
    //在堆棧頂部載入任何矩陣.參數(shù):4*4矩陣
    void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
    //矩陣乘以矩陣堆棧頂部矩陣,相乘結(jié)果存儲到堆棧的頂部
    void GLMatrixStack::MultMatrix(const M3DMatrix44f);
    //使用GetMatrix 函數(shù)獲取矩陣堆棧頂部的值,這個函數(shù)可以進(jìn)行兩次重載,以適應(yīng)GLShaderMananger的使?用,或者僅僅是獲取頂部矩陣的副本
    const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
    void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);
    
    1. 壓棧.出棧
      一個矩陣的真正價值在于通過壓棧操作存儲一個狀態(tài),然后通過出?;謴?fù)這個狀態(tài)。
    //將當(dāng)前矩陣壓?入堆棧
    void GLMatrixStack::PushMatrix(void);
    //將M3DMatrix44f 矩陣對象壓入當(dāng)前矩陣堆棧
    void PushMatrix(const M3DMatrix44f mMatrix);
    //將GLFame 對象壓入矩陣對象
    void PushMatrix(GLFame &frame);
    //出棧(出棧指的是移除頂部的矩陣對象) 
    void GLMatrixStack::PopMatrix(void);
    

    3.仿射變換
    GLMatrixStack類也內(nèi)建了對創(chuàng)建旋轉(zhuǎn)、平移和縮放矩陣的支持。相應(yīng)的函數(shù)如下:

    //Rotate 函數(shù)angle參數(shù)是傳遞的度數(shù),而不是弧度
    void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
    void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z);
    void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);
    

    這三個函數(shù)都可以創(chuàng)建相應(yīng)的矩陣,然后用這個矩陣乘以矩陣堆棧頂部的元素,實際上就是對當(dāng)前矩陣添加變換。

  • 矩陣堆棧的使用完整例子,代碼如下:

#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"

#include <math.h>
#include <stdio.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLShaderManager     shaderManager;          // 著色器管理器
GLMatrixStack       modelViewMatrix;        // 模型視圖矩陣
GLMatrixStack       projectionMatrix;       // 投影矩陣
GLFrustum           viewFrustum;            // 視景體
GLGeometryTransform transformPipeline;      // 幾何圖形變換管道
GLTriangleBatch     torusBatch;             // 花托批處理
//角色幀 照相機(jī)角色幀
GLFrame             cameraFrame;

void SetupRC()
{
    // 初始化著色器管理器
    shaderManager.InitializeStockShaders();
    
    //開啟深度測試
    glEnable(GL_DEPTH_TEST);
    
    //設(shè)置清屏顏色到顏色緩存區(qū)
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
    //繪制甜甜圈
    gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);
    
}


// 屏幕更改大小或已初始化
void ChangeSize(int nWidth, int nHeight)
{
    glViewport(0, 0, nWidth, nHeight);
    
    // 創(chuàng)建投影矩陣,。
    viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
    
    //并將其加載到投影矩陣堆棧上
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    // 設(shè)置變換管道以使用兩個矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
    //初始化GLGeometryTransform 的實例transformPipeline.通過將它的內(nèi)部指針設(shè)置為模型視圖矩陣堆棧 和 投影矩陣堆棧實例,來完成初始化
    //當(dāng)然這個操作也可以在SetupRC 函數(shù)中完成,但是在窗口大小改變時或者窗口創(chuàng)建時設(shè)置它們并沒有壞處。而且這樣可以一次性完成矩陣和管線的設(shè)置。
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}


//進(jìn)行調(diào)用以繪制場景
void RenderScene(void)
{
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    
    // 基于時間動畫
    static CStopWatch   rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    // 清除顏色緩存區(qū)和深度緩沖區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //**3、設(shè)置照相機(jī)矩陣
    M3DMatrix44f mCamera;
    //**3、從camraFrame中獲取一個4*4的矩陣;
    cameraFrame.GetCameraMatrix(mCamera);
    //**3、將照相機(jī)矩陣壓入模型視圖堆棧中
    modelViewMatrix.PushMatrix(mCamera);
    
    // 繪制旋轉(zhuǎn)甜甜圈
    //modelViewMatrix 頂部矩陣沿著z軸移動2.5單位
    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);
    
    modelViewMatrix.PushMatrix();
    
    //modelViewMatrix 頂部矩陣旋轉(zhuǎn)yRot度
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
  
    //使用平面著色器 變換管道中的投影矩陣 和 變換矩陣 相乘的矩陣,指定甜甜圈顏色
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(),vTorusColor);
    
    //開始繪制
    torusBatch.Draw();
    
    // 恢復(fù)modelViewMatrix矩陣,移除矩陣堆棧
    //使用PopMatrix推出剛剛變換的矩陣,然后恢復(fù)到單位矩陣
    modelViewMatrix.PopMatrix();
    
    //**恢復(fù)矩陣**   push跟pop一一對應(yīng),push幾次就要pop幾次。
    modelViewMatrix.PopMatrix();
    
    // 執(zhí)行緩存區(qū)交換
    glutSwapBuffers();
    
    // 告訴glut在顯示一遍
    glutPostRedisplay();
}


int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800,600);
    
    glutCreateWindow("OpenGL SphereWorld");
    
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    glutMainLoop();    
    return 0;
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容