一步步學(xué)習(xí)計算機(jī)視覺in IOS(四)一個完整的OpenGL工程解析

經(jīng)過前面幾個頁面的總結(jié),我們知道了OpenGL渲染的基本知識?,F(xiàn)在,就讓我們來學(xué)習(xí)一個完整的OpenGL工程。學(xué)習(xí)其基本架構(gòu)吧

main

main函數(shù)是OpenGL工程的入口,其負(fù)責(zé)OpenGL的初始化,runloop的承接等等功能,這部分在大多數(shù)的OpenGL工程中是一致的??梢院唵蔚膹?fù)制粘貼:


int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
//初始化GLUT
    glutInit(&argc, argv); 
/* glutInitDisplayMode 定義顯示方式
顏色模式:GLUT_RGBA或者GLUT_RGB。指定一個RGBA窗口,這是一個默認(rèn)的顏色模式。
緩沖區(qū):  GLUT_SINGLE.單緩沖區(qū)窗口; GLUT_DOUBLE.雙緩沖區(qū)窗口
是否允許深度測試:GLUT_DEPTH
*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
// glutInitWindowSize 定義顯示窗口大小
    glutInitWindowSize(800,600);
// glutCreateWindow 創(chuàng)建窗口
    glutCreateWindow("OpenGL SphereWorld");
    
// 注冊函數(shù)指針,這個會在下面細(xì)講
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpeacialKeys);
    
//glewInit 初始化glew庫,用來抹平不同平臺openGL的差異
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
 //用戶自定義的初始化   
    SetupRC();
 //啟動mainloop
    glutMainLoop();    
    return 0;
}
重要函數(shù)

數(shù)據(jù)結(jié)構(gòu)解析

OpenGL提供了一系列數(shù)據(jù)結(jié)構(gòu),下面讓我們依次解釋下面的數(shù)據(jù)類型和他們提供的函數(shù)。

1)管理類
1.1)GLShaderManager 著色器管理器

OpenGL在提交一個幾何圖形進(jìn)行渲染之前必須選定一個著色器。對于初學(xué)者,我們可以使用GLShaderManager來調(diào)用OpenGL提供的一些固定著色器。

GLShaderManager     shaderManager;              // 著色器管理器
//************************
// In SetupRC
shaderManager.InitializeStockShaders();   // 固定的初始化套路
//************************
//In  RenderScene
 shaderManager.UseStockShader(              // 使用平面著色器,后面一般跟著draw方法
GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor);

shaderManager.UseStockShader(          // 使用點光源著色器,注意這里不同的著色器后面參數(shù)不同
GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), 
vLightPos,
 vTorusColor);
1.2)幾何體容器 GLBatch

GLBatch用來管理要渲染的幾何體, 其有很多子類,最常用的還是三角形管理類GLTriangleBatch

GLTriangleBatch     sphereBatch;            //小球
GLBatch                floorBatch;          //地板
//************************
// In SetupRC 
// 配置頂點
    floorBatch.Begin(GL_LINES, 324); // 初始化管理器,GL_LINES表示線性圖元,324表示有324個頂點
    for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {
        floorBatch.Vertex3f(x, -0.55f, 20.0f); // 添加頂點數(shù)據(jù)
        ......
    }
    floorBatch.End();

    gltMakeSphere(sphereBatch, 0.1f, 26, 13); 直接生成一個球形

//************************
// In RenderScene 
//啟動繪制
floorBatch.draw();
sphereBatch.draw();
2) 矩陣相關(guān)

首先,我們要知道,在OpenGL的世界里,物體的位置是存儲為矩陣的,物體的移動,平移,旋轉(zhuǎn)等等操作是通過矩陣相乘實現(xiàn)的,此外,為了讓屏幕上的展示產(chǎn)生立體效果,需要通過透視投影進(jìn)行轉(zhuǎn)化,這同樣是通過透視矩陣 x 位置矩陣來實現(xiàn)的。

2.1)GLMatrixStack 矩陣堆棧

由于矩陣需要做多種操作,OpenGL使用GLMatrixStack矩陣堆棧來存儲矩陣。

GLMatrixStack       modelViewMatrix;        // 模型視圖矩陣棧
GLMatrixStack       projectionMatrix;       // 投影矩陣棧
//************************
// In RenderScene  
   //加入objectFrame
    modelViewMatrix.PushMatrix(objectFrame);
   //使得大球位置平移(3.0)向屏幕里面
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //8.壓棧(復(fù)制棧頂)
    modelViewMatrix.PushMatrix();
    //自轉(zhuǎn)
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //配置GLShaderManager著色器
    //GLBatch開始繪制
   //結(jié)果出棧
GLMatrixStack的方法

作為一個堆棧,GLMatrixStack提供了最基本的 push pop 方法:

void PushMatrix(GLFrame& frame)  //GLFrame轉(zhuǎn)成4*4矩陣入棧
void PushMatrix(const M3DMatrix44f mMatrix) //入棧一個4*4矩陣
void PushMatrix(void) //復(fù)制棧頂矩陣并再次入棧

void PopMatrix(void) //出棧

我們還可以看到 load方法,它用于替換棧頂矩陣

void LoadMatrix(GLFrame& frame)  //GLFrame轉(zhuǎn)成4*4矩陣替換棧頂矩陣
void LoadMatrix(const M3DMatrix44f mMatrix) //替換棧頂矩陣
void LoadIdentity(void) //一個4*4單位矩陣替換棧頂矩陣

GLMatrixStack的引入就是為了方便計算,它提供了一系列的矩陣處理方法,要注意的是,下面這些操作默認(rèn)將操作結(jié)果替換棧頂矩陣而不是入棧:

第一個就是矩陣相乘MultMatrix

void MultMatrix(const M3DMatrix44f mMatrix) // 棧頂矩陣 x 入?yún)⒕仃噈Matrix,結(jié)果**替換棧頂矩陣**

然后就是依賴矩陣相乘的模型變化:

void Scale(GLfloat x, GLfloat y, GLfloat z) {} // 三向縮放,結(jié)果**替換棧頂矩陣**
void Translate(GLfloat x, GLfloat y, GLfloat z) {}// 三向平移,結(jié)果**替換棧頂矩陣**
void Rotate(GLfloat x, GLfloat y, GLfloat z) {}// 三向旋轉(zhuǎn),結(jié)果**替換棧頂矩陣**

Example:

LoadMatrix(A)       stack = [A]
PushMatrix()         stack = [A, A]
LoadMatrix(B)       stack = [B, A]
PopMatrix()          stack = [A]


LoadMatrix(A)       stack = [A]
PushMatrix()         stack = [A, A]
MultMatrix(B)       stack = [AB, A] // 
PopMatrix()          stack = [A]
2.2)GLFrustum 視景體和投影矩陣

為了在2D屏幕上產(chǎn)生3D視覺效果,投影變換,或者投影矩陣是必不可少的。投影矩陣的生成是由GLFrustum負(fù)責(zé)的, 而結(jié)果會存在在一個GLMatrixStack projectionMatrix中 :

GLMatrixStack       projectionMatrix;       // 投影矩陣棧
GLFrustum           viewFrustum;            // 視景體
//************************
// In ChangeSize 視窗size變化是投影矩陣也會變化 (其實ChangeSize函數(shù)除了一句設(shè)置視口外,都是用來生成,更新和存儲投影矩陣)
...
//2.創(chuàng)建投影矩陣
    viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
    //并將其加載到投影矩陣堆棧上
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
...
//************************
// In RenderScene  
// 投影矩陣應(yīng)用在著色器中 
// transformPipeline.GetProjectionMatrix()
shaderManager.UseStockShader(
GLT_SHADER_POINT_LIGHT_DIFF, 
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);

GLFrustum中投影矩陣的生成
投影矩陣的生成方法如下:

viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
 ->
void SetPerspective(float fFov, float fAspect, float fNear, float fFar)
image.png

fFov是視角,fAspect是要展示的視窗的寬高比,fNear是展示視窗的視距,fFar是遠(yuǎn)視窗的視距。

2.3)GLGeometryTransform 幾何圖形變換管道

GLGeometryTransform可以類比成一個存儲模型視圖矩陣棧投影矩陣棧的容器。在GLShaderManager中,使用GLGeometryTransform來方便的獲取這兩個值。

GLMatrixStack       modelViewMatrix;        // 模型視圖矩陣棧
GLMatrixStack       projectionMatrix;       // 投影矩陣棧
GLGeometryTransform transformPipeline;      // 幾何圖形變換管道
//************************
//In ChangeSize
....矩陣棧的初始化
//將兩個矩陣棧塞入管道
 transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//************************
// In RenderScene  
    //在GLShaderManager中使用這兩個矩陣棧
shaderManager.UseStockShader(
GLT_SHADER_POINT_LIGHT_DIFF, 
transformPipeline.GetModelViewMatrix(),
 transformPipeline.GetProjectionMatrix(), 
vLightPos, vSphereColor);
2.4)GLFrame 參照幀

一個視圖上可能同時存在很多要渲染的幾何體,如果我們想實現(xiàn)一個屏幕拉遠(yuǎn)的功能,對每一個幾何體進(jìn)行直接矩陣操作無疑是一個很浪費的行為。OpenGL可以為渲染角色提供參照幀,或者成為本地對象坐標(biāo)系。它既可以表示物體,也可以表示觀察者:

//角色幀 
GLFrame   cameraFrame;
//照相機(jī)角色幀
GLFrame  objectFrame;

//**添加附加隨機(jī)球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];

//************************
// In SetupRC 
// 將物體坐標(biāo)前移5f(在z軸上移動,MoveForward值越大,物體視圖越小,表示距離觀察者越遠(yuǎn))
 objectFrame.MoveForward(5.0f);

for (int i = 0; i < NUM_SPHERES; i++) {
        
        //y軸不變,X,Z產(chǎn)生隨機(jī)值
        GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        
        //在y方向,將球體設(shè)置為0.0的位置,這使得它們看起來是飄浮在眼睛的高度
        //對spheres數(shù)組中的每一個頂點,設(shè)置frame的頂點數(shù)據(jù)
        spheres[i].SetOrigin(x, 2.0f, z);
    }

//************************
// In RenderScene  
 //加入objectFrame
    modelViewMatrix.PushMatrix(objectFrame);
.....一系列矩陣變換

void SetupRC()

SetupRC用來初始化渲染配置,在程序運(yùn)行過程中只會執(zhí)行一次。
它主要負(fù)責(zé)GLShaderManager和GLBatch的初始化,頂點數(shù)據(jù)的生成,GLFrame的調(diào)整, 配置處理等操作。

void SetupRC()
{
    //清除顏色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
   // GLShaderManager初始化
    shaderManager.InitializeStockShaders();
   //移動渲染物體的參考坐標(biāo)
    objectFrame.MoveForward(15.0f);
   //開啟深度測試
    glEnable(GL_DEPTH_TEST);
   //設(shè)置GLBatch中的頂點數(shù)據(jù)
     floorBatch.Begin(GL_LINES, 320);
    for(...) {
        floorBatch.Vertex3f(x, -0.55f, 20.0f);
        .....
    }
    floorBatch.End();
 //使用固定類型直接配置頂點
   gltMakeSphere(torusBatch, 0.4f, 40, 80);
}
void ChangeSize(int nWidth, int nHeight)

當(dāng)?shù)谝淮芜\(yùn)行,以及視窗大小發(fā)生變化時會調(diào)用,主要負(fù)責(zé)視口調(diào)整以及新的投影矩陣的生成

//屏幕更改大小或已初始化
void ChangeSize(int nWidth, int nHeight)
{
    //1.設(shè)置視口
    glViewport(0, 0, nWidth, nHeight);
    
    //2.創(chuàng)建投影矩陣
    viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
    //viewFrustum.GetProjectionMatrix()  獲取viewFrustum投影矩陣
    //并將其加載到投影矩陣堆棧上
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

    //3.設(shè)置變換管道以使用兩個矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
    //初始化GLGeometryTransform 的實例transformPipeline.通過將它的內(nèi)部指針設(shè)置為模型視圖矩陣堆棧 和 投影矩陣堆棧實例,來完成初始化
    //當(dāng)然這個操作也可以在SetupRC 函數(shù)中完成,但是在窗口大小改變時或者窗口創(chuàng)建時設(shè)置它們并沒有壞處。而且這樣可以一次性完成矩陣和管線的設(shè)置。
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}

void RenderScene(void)

開始繪制場景時調(diào)用,主要負(fù)責(zé)位置數(shù)據(jù)想渲染數(shù)據(jù)的轉(zhuǎn)換,對矩陣地操作主要集中在這個函數(shù)中。

 //清除顏色緩存區(qū)和深度緩沖區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//配置顏色值
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f};
//配置光源
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
  //加入objectFrame
    modelViewMatrix.PushMatrix(objectFrame);
  //使得大球位置平移(3.0)向屏幕里面
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //壓棧(復(fù)制棧頂)
    modelViewMatrix.PushMatrix();
    //大球自轉(zhuǎn)
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //指定合適的著色器(點光源著色器)
    shaderManager.UseStockShader(
      GLT_SHADER_POINT_LIGHT_DIFF, 
      transformPipeline.GetModelViewMatrix(),
    transformPipeline.GetProjectionMatrix(), 
    vLightPos, vTorusColor);
  //繪制大球
    torusBatch.Draw();
//繪制完畢則Pop
    modelViewMatrix.PopMatrix();
    //執(zhí)行緩存區(qū)交換
    glutSwapBuffers();
    //完成繪制
    glutPostRedisplay();
?著作權(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ù)。

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