第十三節(jié)—GLTools自帶圖形繪制和視變換

本文為L_Ares個人寫作译荞,包括圖片皆為個人親自操作瓤的,以任何形式轉(zhuǎn)載請表明原文出處。

OpenGL的一些庫中是有一些自帶圖形的吞歼,用于一些簡單的圖形繪制是很好用的圈膏,另外本節(jié)代碼也會為下一節(jié)做一下鋪墊。

直接上代碼篙骡,代碼中都有非常詳細的注釋稽坤,而且都是經(jīng)過測試可以跑起來的丈甸。


//
//  main.cpp
//  07矩陣堆棧
//
//  Created by EasonLi on 2020/8/29.
//  Copyright ? 2020 EasonLi. All rights reserved.
//

#include <stdio.h>

#pragma mark - 引用類

//著色器管理器。負責(zé)管理和創(chuàng)建著色器管理器尿褪。里面有一些固定的著色器睦擂,可用于初級操作
#include "GLShaderManager.h"
//GLTool工具類,里面包含了GLTool大多數(shù)類似C語言的函數(shù)
#include "GLTools.h"
//矩陣堆棧類茫多。包含了矩陣堆棧的操作
#include "GLMatrixStack.h"
//管道類,用于更加方便的管理矩陣
#include "GLGeometryTransform.h"
//用于設(shè)置觀察者或者物體的位置
#include "GLFrame.h"
//投影矩陣獲取
#include "GLFrustum.h"
//三角形批次類
#include "GLBatch.h"
//3d數(shù)學(xué)類
#include "math3d.h"

//宏定義判斷系統(tǒng)忽刽,引入GLUT
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#pragma mark - 公共變量

//著色器管理器
GLShaderManager shaderManager;
//幾何變換管道
GLGeometryTransform transformPipeline;

//模型視圖矩陣
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;

//觀察者(相機)位置
GLFrame cameraFrame;
//物體世界坐標(biāo)位置
GLFrame objectFrame;

//視景體天揖,用來構(gòu)造投影矩陣
GLFrustum viewFrustum;

//三角形批次類
//球
GLTriangleBatch sphereBatch;
//環(huán)
GLTriangleBatch torusBatch;
//圓柱
GLTriangleBatch cylinderBatch;
//錐
GLTriangleBatch coneBatch;
//磁盤
GLTriangleBatch diskBatch;

//顏色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};
GLfloat vBlack[] = {0.f,0.f,0.f,1.f};

//記錄點擊空格的次數(shù)
int step = 0;

#pragma mark - 函數(shù)

/**
 設(shè)置窗口
 */
void ChangeSize(int w,int h)
{
    
    if (h == 0) {
        h = 1;
    }
    
    //設(shè)置視口
    glViewport(0, 0, w, h);
    
    //設(shè)置透視投影
    //1.觀察者垂直視角度數(shù)。2.寬高比跪帝。3.視點與近剪裁面距離。4.視點與遠剪裁面距離
    viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
    
    //projectionMatrix矩陣堆棧加載透視投影矩陣
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //modelViewMatrix堆棧矩陣加載一個單元矩陣。其實不加載也沒事拒啰,源碼中可以看到胆数,已經(jīng)幫忙加載了一個了。
    modelViewMatrix.LoadIdentity();
    
    //通過GLGeometryTransform管理矩陣堆棧(模型視圖矩陣堆棧黎泣、投影矩陣堆棧)
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    
}

/**
 設(shè)置渲染環(huán)境
 */
void SetUpRC ()
{
    
    //設(shè)置清屏顏色
    glClearColor(0.3f, 0.3f, 0.3f, 1.f);
    //著色器管理器初始化
    shaderManager.InitializeStockShaders();
    
    //開啟需要用到的環(huán)境
    //開啟深度測試
    glEnable(GL_DEPTH_TEST);
    
    //設(shè)置觀察者和物體的距離恕刘,保證物體都能看得到
    //這里有兩種方式(1)(2)
    //(1)將觀察者(攝像機)位置沿其Z軸向內(nèi)部移動15個單位。負數(shù)往里面移動抒倚,正數(shù)向外面移動褐着。
    //cameraFrame.MoveForward(-15.f);
    
    //(2)將物體移動15個單位,觀察者(攝像機)不動
    objectFrame.MoveForward(15.f);
    
    //這里直接使用一下GLTools里面給過的圖形
    /**
     球:
     gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
     參數(shù):
     (1)sphereBatch:三角形批次類對象
     (2)fRadius:球體半徑
     (3)iSlices:從球體底部堆疊到球體頂部的三角形帶的數(shù)量托呕『兀可以看出球體是一圈圈的三角形帶組成的。
     (4)iStacks:圍繞球體一圈排列的三角形片段對數(shù)
     建議:一個對稱性較好的球體一般滿足片段數(shù)量是三角形帶數(shù)量的2倍项郊。iSlices * 2 = iStacks
     繪制球體都是圍繞Z軸的馅扣,因為這樣Z軸的正方向(+)就是球體頂部,Z軸負方向(-)就是球體底部着降。
     */
    gltMakeSphere(sphereBatch, 3.f, 10, 20);
    
    /**
     環(huán):
     gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
     參數(shù):
     (1)torusBatch:三角形批次類對象
     (2)majorRadius:圓環(huán)中心到外邊緣的半徑(大圓半徑)
     (3)minorRadius:圓環(huán)中心到內(nèi)邊緣的半徑(小圓半徑)
     (4)numMajor:大圓的三角形數(shù)量
     (5)numMinor:小圓的三角形數(shù)量
     */
    gltMakeTorus(torusBatch, 3.f, 1, 16, 16);
    
    /**
     圓柱:
     gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     參數(shù):
     (1)cylinderBatch:三角形批次類對象
     (2)baseRadius:底部圓半徑
     (3)topRadius:頂部圓半徑
     (4)fLength:圓柱體的長度(高度)
     (5)numSlices:圍繞Z軸的三角形帶的對數(shù)
     (6)numStacks:從圓柱底到圓柱頂?shù)娜切螏У臄?shù)量
     */
    gltMakeCylinder(cylinderBatch, 3.f, 3.f, 3.f, 16, 3);
    
    /**
     錐:特殊的圓柱
     gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     參數(shù):
     (1)cylinderBatch:三角形批次類對象
     (2)baseRadius:底部圓的半徑
     (3)topRadius:頂部圓半徑差油,錐體直接給0就行了
     (4)fLength:錐體長度(高度)
     (5)numSlices:圍繞Z軸的三角形帶的對數(shù)
     (6)numStacks:從圓柱底到圓柱頂?shù)娜切螏У臄?shù)量
     */
    gltMakeCylinder(coneBatch, 3.f, 0.f, 3.f, 16, 3);
    
    /**
     磁盤:
     gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
     參數(shù):
     (1)diskBatch:三角形批次類對象
     (2)innerRadius:內(nèi)部圓半徑
     (3)outerRadius:外部圓半徑
     (4)nSlices:圍繞Z軸的三角形對的數(shù)量
     (5)nStacks:從內(nèi)邊緣到外邊緣三角形帶的對數(shù)
     */
    gltMakeDisk(diskBatch, 1.f, 3.f, 16, 4);
    
}

/**
 利用三角形批次類進行繪制
 */
void DrawBatch(GLTriangleBatch *dBatch)
{
    
    //繪制圖形
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vRed);
    
    //這里用的是指針,看清楚上面任洞,不能用點方法
    dBatch->Draw();
    
    //繪制黑色線條
    //這里就需要開啟顏色混合厌殉、多邊形偏移、抗鋸齒等功能
    
    //開啟多邊形偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    //設(shè)置偏移的面和模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //指定偏移量
    glPolygonOffset(-1.f, -1.f);
    
    //開啟混合
    glEnable(GL_BLEND);
    //設(shè)置混合因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //開啟線段模式抗鋸齒(簡單的抗鋸齒)
    glEnable(GL_LINE_SMOOTH);
    
    //可以設(shè)置一下線段的寬度
    glLineWidth(3.f);
    
    //利用平面著色器畫黑線
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    dBatch->Draw();
    
    //繪制完成記得關(guān)閉之前開啟的功能侈咕,回復(fù)圖形填充方式,并恢復(fù)線段寬度
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    glLineWidth(1.f);
    
}

//觀察者移動(攝像機)公罕,物體不動
void CameraMoveObjNo()
{
    //復(fù)制棧頂?shù)闹担⑶覊喝霔m?    modelViewMatrix.PushMatrix();
    
    //獲取觀察者(攝像頭)矩陣
    //先定義一個矩陣耀销,方便一會存儲獲取到的觀察者(攝像頭)矩陣
    M3DMatrix44f mCamera;
    //從cameraFrame中獲取它棧頂?shù)木仃嚶ゾ欤缓筚x值給mCamera铲汪,好用來做矩陣變換,這樣做能保證多圖形的情況下罐柳,不改變原
    //cameraFrame的棧頂矩陣掌腰,而用一份復(fù)制出來的操作,不會影響到其他的圖形繪制
    cameraFrame.GetCameraMatrix(mCamera);
    //讓modelViewMatrix模型視圖矩陣和觀察者(攝像頭)矩陣相乘张吉,發(fā)生第一步變化齿梁,相乘的結(jié)果會存儲到
    //modelViewMatrix棧頂里面
    modelViewMatrix.MultMatrix(mCamera);
    
    //獲取物體的矩陣
    //先定義一個矩陣,也是方便存儲獲取到的物體仿射變換后的矩陣
    M3DMatrix44f mObject;
    //從objectFrame中獲取其棧頂?shù)木仃嚢褂迹⑶屹x值給mObject勺择,原理和上面的一樣
    objectFrame.GetMatrix(mObject);
    //讓modelViewMatrix模型視圖矩陣和mObject相乘,結(jié)果矩陣依然存儲在modelViewMatrix棧頂
    modelViewMatrix.MultMatrix(mObject);
    
}

//物體移動伦忠,觀察者(攝像機)不動
void ObjMoveCameraNo()
{
    
    modelViewMatrix.PushMatrix(objectFrame);
    
}

/**
 渲染
 */
void RenderScene()
{
    
    //清空緩沖區(qū)
    //雖然模版緩沖區(qū)都沒用到省核,但是養(yǎng)成全部清空的好習(xí)慣肯定不會錯
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //當(dāng)觀察者移動(攝像機),而物體不移動的時候昆码,對應(yīng)了SetUpRC中的(1)
    //CameraMoveObjNo();
    
    //當(dāng)觀察者不動气忠,物體移動的時候,對應(yīng)了SetUpRC中的(2)
    ObjMoveCameraNo();
    
    
    //到上面這一步赋咽,矩陣的變換已經(jīng)都做完了旧噪,下面要繪制了
    //判斷空格點擊次數(shù),更換不同的圖形
    switch (step) {
            
        case 0:
            DrawBatch(&sphereBatch);
            break;
            
        case 1:
            DrawBatch(&torusBatch);
            break;
            
        case 2:
            DrawBatch(&cylinderBatch);
            break;
        
        case 3:
            DrawBatch(&coneBatch);
            break;
            
        case 4:
            DrawBatch(&diskBatch);
            break;
            
    }
    
    //繪制完成一個圖形脓匿,就把棧頂?shù)木仃嚳梢訮op出去了舌菜,不要影響下一個圖形的繪制
    modelViewMatrix.PopMatrix();
    
    //交換緩沖區(qū)
    glutSwapBuffers();
    
}

/**
 設(shè)置特殊鍵位(上下左右)
 */
void SpecialKeys(int key,int x,int y)
{
    
    //這里還是移動世界坐標(biāo)系,因為畫的東西都有太多的頂點了亦镶,移動物體的成本太高日月,效果是一樣的。
    switch (key) {
            
        case GLUT_KEY_UP:
            objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.f, 0.f, 0.f);
            break;
            
        case GLUT_KEY_DOWN:
            objectFrame.RotateWorld(m3dDegToRad(5.f), 1.f, 0.f, 0.f);
            break;
            
        case GLUT_KEY_LEFT:
            objectFrame.RotateWorld(m3dDegToRad(-5.f), 0.f, 1.f, 0.f);
            break;
            
        case GLUT_KEY_RIGHT:
            objectFrame.RotateWorld(m3dDegToRad(5.f), 0.f, 1.f, 0.f);
            break;
            
    }
    
    //移動完了要發(fā)送渲染信號
    glutPostRedisplay();
    
}

/**
 設(shè)置鍵盤鍵位(空格)
 */
void SpaceKey(unsigned char key,int x,int y)
{
    
    //拿空格鍵的ASCII碼來判定點擊的是鍵盤上的空格鍵
    if (key == 32) {
        step++;
        if(step > 4) step = 0;
    }
    
    switch (step) {
        case 0:
            glutSetWindowTitle("Sphere球");
            break;
        case 1:
            glutSetWindowTitle("Torus圓環(huán)");
            break;
        case 2:
            glutSetWindowTitle("Cylinder圓柱");
            break;
        case 3:
            glutSetWindowTitle("Cone錐");
            break;
        case 4:
            glutSetWindowTitle("Disk磁盤");
            break;
    }
    //發(fā)送重新渲染的信號
    glutPostRedisplay();
    
}

#pragma mark - main

int main(int argc,char *argv[])
{
    
    //設(shè)置工作目錄和項目目錄都在/Resouce下面缤骨,GLUT優(yōu)先級做過爱咬,但是手動確保一定
    gltSetWorkingDirectory(argv[0]);
    
    //GLUT初始化
    glutInit(&argc, argv);
    
    //設(shè)置顯示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_STENCIL);
    
    //設(shè)置窗口大小
    glutInitWindowSize(600, 600);
    
    //創(chuàng)建窗口并命名
    glutCreateWindow("Sphere球");
    
    //注冊函數(shù)
    //注冊窗口
    glutReshapeFunc(ChangeSize);
    //注冊渲染
    glutDisplayFunc(RenderScene);
    //注冊特殊鍵位
    glutSpecialFunc(SpecialKeys);
    //注冊空格鍵位
    glutKeyboardFunc(SpaceKey);
    
    //初始化Glew庫
    GLenum status = glewInit();
    if (status != GLEW_OK) {
        printf("glew init error : %s \n",glewGetErrorString(status));
    }
    
    //設(shè)置渲染環(huán)境
    SetUpRC();
    
    //構(gòu)建本地循環(huán)
    glutMainLoop();
    
    return 0;
    
}

顯示效果我就不貼圖了,有問題的話可以留言绊起。

這上面void SetUpRC()void RenderScene()中的(1)(2)的變換精拟,就是視變換的結(jié)果,說的通俗點虱歪,就是山不向你走來蜂绎,你就向山走去。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笋鄙,一起剝皮案震驚了整個濱河市师枣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萧落,老刑警劉巖践美,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洗贰,死亡現(xiàn)場離奇詭異,居然都是意外死亡陨倡,警方通過查閱死者的電腦和手機敛滋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兴革,“玉大人绎晃,你說我怎么就攤上這事≡忧” “怎么了庶艾?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長解阅。 經(jīng)常有香客問我落竹,道長泌霍,這世上最難降的妖魔是什么货抄? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮朱转,結(jié)果婚禮上蟹地,老公的妹妹穿的比我還像新娘。我一直安慰自己藤为,他們只是感情好怪与,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缅疟,像睡著了一般分别。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上存淫,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天耘斩,我揣著相機與錄音,去河邊找鬼桅咆。 笑死括授,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岩饼。 我是一名探鬼主播荚虚,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼籍茧!你這毒婦竟也來了版述?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤寞冯,失蹤者是張志新(化名)和其女友劉穎院水,沒想到半個月后腊徙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡檬某,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年撬腾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恢恼。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡民傻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出场斑,到底是詐尸還是另有隱情漓踢,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布漏隐,位于F島的核電站喧半,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏青责。R本人自食惡果不足惜挺据,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脖隶。 院中可真熱鬧扁耐,春花似錦、人聲如沸产阱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽构蹬。三九已至王暗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庄敛,已是汗流浹背俗壹。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铐姚,地道東北人策肝。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像隐绵,于是被迫代替她去往敵國和親之众。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344