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

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

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

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

    • 我們可以調(diào)用math3d庫(kù)中m3dTranslationMatrix44函數(shù)來(lái)使用變換矩陣
      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)
      
    • 綜合變換:將兩個(gè)矩陣相乘并返回結(jié)果
      void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
      

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

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

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

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

// 設(shè)置圖元繪制時(shí)的投影方式
GLFrustum           viewFrustum;
//  存儲(chǔ)著?色器?管理理?工具類.
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);
}

//召喚場(chǎng)景
void RenderScene(void)
{
    //清除屏幕屹耐、深度緩存區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //1.建立基于時(shí)間變化的動(dòng)畫
    static CStopWatch rotTimer;
    //當(dāng)前時(shí)間 * 60s
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //2.矩陣變量
    /*
     mTranslate: 平移
     mRotate: 旋轉(zhuǎn)
     mModelview: 模型視圖
     mModelViewProjection: 模型視圖投影MVP
     */
    M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
    
    //創(chuàng)建一個(gè)4*4矩陣變量,將花托沿著Z軸負(fù)方向移動(dòng)2.5個(gè)單位長(zhǎng)度
    m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
    
    //創(chuàng)建一個(gè)4*4矩陣變量,將花托在Y軸上渲染yRot度惶岭,yRot根據(jù)經(jīng)過(guò)時(shí)間設(shè)置動(dòng)畫幀率
     m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
    
    //為mModerView 通過(guò)矩陣旋轉(zhuǎn)矩陣寿弱、移動(dòng)矩陣相乘,將結(jié)果添加到mModerView上
    m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
    
    // 將投影矩陣乘以模型視圖矩陣按灶,將變化結(jié)果通過(guò)矩陣乘法應(yīng)用到mModelViewProjection矩陣上
    //注意順序: 投影 * 模型 != 模型 * 投影
     m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
  
    //繪圖顏色
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    
    //通過(guò)平面著色器提交矩陣症革,和顏色。
    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.形成一個(gè)球
    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è)類的構(gòu)造函數(shù)允許指定堆棧的最大深度噪矛,默認(rèn)的堆棧深度為64,這個(gè)矩陣堆棧在初始化時(shí)已經(jīng)在堆棧中包含了單元矩陣铺罢。
    GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
    //在堆棧頂部載?一個(gè)單元矩陣
    void GLMatrixStack::LoadIdentity(void);
    //在堆棧頂部載入任何矩陣.參數(shù):4*4矩陣
    void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
    //矩陣乘以矩陣堆棧頂部矩陣艇挨,相乘結(jié)果存儲(chǔ)到堆棧的頂部
    void GLMatrixStack::MultMatrix(const M3DMatrix44f);
    //使用GetMatrix 函數(shù)獲取矩陣堆棧頂部的值,這個(gè)函數(shù)可以進(jìn)行兩次重載韭赘,以適應(yīng)GLShaderMananger的使?用缩滨,或者僅僅是獲取頂部矩陣的副本
    const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
    void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);
    
    1. 壓棧.出棧
      一個(gè)矩陣的真正價(jià)值在于通過(guò)壓棧操作存儲(chǔ)一個(gè)狀態(tài),然后通過(guò)出椚埃恢復(fù)這個(gè)狀態(tài)脉漏。
    //將當(dāng)前矩陣壓?入堆棧
    void GLMatrixStack::PushMatrix(void);
    //將M3DMatrix44f 矩陣對(duì)象壓入當(dāng)前矩陣堆棧
    void PushMatrix(const M3DMatrix44f mMatrix);
    //將GLFame 對(duì)象壓入矩陣對(duì)象
    void PushMatrix(GLFame &frame);
    //出棧(出棧指的是移除頂部的矩陣對(duì)象) 
    void GLMatrixStack::PopMatrix(void);
    

    3.仿射變換
    GLMatrixStack類也內(nèi)建了對(duì)創(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);
    

    這三個(gè)函數(shù)都可以創(chuàng)建相應(yīng)的矩陣,然后用這個(gè)矩陣乘以矩陣堆棧頂部的元素贼陶,實(shí)際上就是對(duì)當(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();
    
    //開啟深度測(cè)試
    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è)置變換管道以使用兩個(gè)矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
    //初始化GLGeometryTransform 的實(shí)例transformPipeline.通過(guò)將它的內(nèi)部指針設(shè)置為模型視圖矩陣堆棧 和 投影矩陣堆棧實(shí)例撮胧,來(lái)完成初始化
    //當(dāng)然這個(gè)操作也可以在SetupRC 函數(shù)中完成桨踪,但是在窗口大小改變時(shí)或者窗口創(chuàng)建時(shí)設(shè)置它們并沒有壞處。而且這樣可以一次性完成矩陣和管線的設(shè)置芹啥。
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}


//進(jìn)行調(diào)用以繪制場(chǎng)景
void RenderScene(void)
{
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    
    // 基于時(shí)間動(dòng)畫
    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中獲取一個(gè)4*4的矩陣墓怀;
    cameraFrame.GetCameraMatrix(mCamera);
    //**3汽纠、將照相機(jī)矩陣壓入模型視圖堆棧中
    modelViewMatrix.PushMatrix(mCamera);
    
    // 繪制旋轉(zhuǎn)甜甜圈
    //modelViewMatrix 頂部矩陣沿著z軸移動(dòng)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一一對(duì)應(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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碴犬,一起剝皮案震驚了整個(gè)濱河市絮宁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌服协,老刑警劉巖绍昂,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異偿荷,居然都是意外死亡治专,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門遭顶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人泪蔫,你說(shuō)我怎么就攤上這事棒旗。” “怎么了撩荣?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵铣揉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我餐曹,道長(zhǎng)逛拱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任台猴,我火速辦了婚禮朽合,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饱狂。我一直安慰自己曹步,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布休讳。 她就那樣靜靜地躺著讲婚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俊柔。 梳的紋絲不亂的頭發(fā)上筹麸,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音雏婶,去河邊找鬼物赶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛留晚,可吹牛的內(nèi)容都是我干的块差。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼憨闰!你這毒婦竟也來(lái)了状蜗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鹉动,失蹤者是張志新(化名)和其女友劉穎轧坎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泽示,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缸血,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了械筛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捎泻。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖埋哟,靈堂內(nèi)的尸體忽然破棺而出笆豁,到底是詐尸還是另有隱情,我是刑警寧澤赤赊,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布闯狱,位于F島的核電站,受9級(jí)特大地震影響抛计,放射性物質(zhì)發(fā)生泄漏哄孤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一吹截、第九天 我趴在偏房一處隱蔽的房頂上張望瘦陈。 院中可真熱鬧,春花似錦波俄、人聲如沸双饥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咏花。三九已至,卻和暖如春阀趴,著一層夾襖步出監(jiān)牢的瞬間昏翰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工刘急, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棚菊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓叔汁,卻偏偏與公主長(zhǎng)得像统求,于是被迫代替她去往敵國(guó)和親检碗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容