OpenGL 學(xué)習(xí) 05 - 花托(剔除/深度測(cè)試/多邊形模式)

學(xué)習(xí)書(shū)籍: OpenGL 超級(jí)寶典(中文第五版) 密碼:fu4w

書(shū)籍源碼:OpenGL 超級(jí)寶典第五版源代碼 密碼:oyb4

環(huán)境搭建:OpenGL 學(xué)習(xí) 01 - Mac 搭建 OpenGL 環(huán)境

之前我們都只是畫(huà)平面圖形曲掰,這節(jié)我們來(lái)畫(huà)一個(gè) 3D 圖形链沼,對(duì)比下平面圖形和 3D 圖形繪制的一些區(qū)別和注意點(diǎn)。

基本概念

花托

一種環(huán)狀的像面包圈一樣的物體牵辣,如果要我們自己去一個(gè)個(gè)去計(jì)算花托的每個(gè)點(diǎn)烟瞧,是要花很多時(shí)間,GLTools 里面已經(jīng)為我們集成好了得到花托頂點(diǎn)數(shù)據(jù)的函數(shù)

//得到花托的頂點(diǎn)數(shù)據(jù)
void gltMakeTorus(
    GLTriangleBatch& torusBatch, // 返回的三角形批次,和普通批次功能類(lèi)似,是專(zhuān)門(mén)畫(huà)三角形的批次
    GLfloat majorRadius, // 中心到外邊緣的半徑
    GLfloat minorRadius, // 外邊緣到內(nèi)邊緣的距離
    GLint numMajor, // 片段數(shù)氮墨,即花托由多少個(gè)片段面組成
    GLint numMinor  // 堆疊數(shù),即每個(gè)片段面由多少個(gè)頂點(diǎn)組成
); 

油畫(huà)法渲染

在默認(rèn)情況下吐葵,我們所渲染的點(diǎn)勇边、線或三角形都會(huì)在屏幕上進(jìn)行光柵化,并且會(huì)按照在組合批次時(shí)的指定順序進(jìn)行排列折联,這樣就會(huì)出現(xiàn)一些問(wèn)題,比如一些三角形在物體背面识颊,我們應(yīng)該是看不到的诚镰,或者前面繪制的三角形可能被后面繪制的三角形所覆蓋。

對(duì)于這些問(wèn)題祥款,可能的解決方法之一是清笨,對(duì)這些三角形進(jìn)行排序,先渲染較遠(yuǎn)的三角形刃跛,再渲染較近的三角形抠艾,這種方式稱(chēng)為“油畫(huà)法”〗瓣迹【油畫(huà)法渲染是非常低效的检号,重疊部分進(jìn)行了多次寫(xiě)操作,三角形排序開(kāi)銷(xiāo)大】

正面和背面剔除

對(duì)正面和背面三角形進(jìn)行區(qū)分的原因之一就是為了進(jìn)行剔除蛙酪。背面剔除能極大提高性能齐苛,且能修正一些上面提出的問(wèn)題。

剔除是在渲染的圖元裝配階段就整體拋棄掉一些三角形桂塞,并且沒(méi)有執(zhí)行任何不恰當(dāng)?shù)墓鈻呕僮鳌?/strong>

深度測(cè)試

深度測(cè)試是另外一種高效消除隱藏表面的技術(shù)凹蜂。深度測(cè)試是在繪制每個(gè)像素時(shí),分配一個(gè)深度值z阁危,表示該點(diǎn)與觀察者的距離【注意玛痊,這里是深度值越大,表示離觀察者越近】狂打,當(dāng)另外一個(gè)像素需要繪制在同一個(gè)位置擂煞,會(huì)先比較深度值,深度值大的說(shuō)明在前面菱父,深度值小的說(shuō)明在后面颈娜。

深度測(cè)試是通過(guò)深度緩沖區(qū)實(shí)現(xiàn)的剑逃,深度緩存區(qū)存儲(chǔ)了屏幕上每個(gè)像素的深度值。要使用深度測(cè)試官辽,一般需要配置渲染模式:

/*初始化渲染模式*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

深度測(cè)試還有一個(gè)很強(qiáng)大的作用蛹磺,就是處理重疊的獨(dú)立對(duì)象,這是剔除技術(shù)無(wú)法處理的一種情況同仆。深度測(cè)試是先渲染離觀察者較近的對(duì)象萤捆,再渲染較遠(yuǎn)的對(duì)象,和“油畫(huà)法”剛好相反俗批。

開(kāi)啟剔除俗或,但未開(kāi)啟深度測(cè)試
只開(kāi)啟深度測(cè)試

多邊形模式

默認(rèn)情況下,多邊形是作為實(shí)心圖形進(jìn)行繪制岁忘,我們可以通過(guò)調(diào)整多邊形模式辛慰,讓多邊形繪制為只有點(diǎn)或輪廓。此外干像,我們也可以在多邊形的兩面分別應(yīng)用這個(gè)多邊形模式

多邊形模式:

  • 實(shí)體(GL_FULL)
  • 輪廓(GL_LINE)
  • 點(diǎn)(GL_POINT)

多邊形模式應(yīng)用面:

  • 正面(GL_FRONT)
  • 背面(GL_BACK)
  • 雙面(GL_FRONT_AND_BACK)
輪廓
點(diǎn)

源碼解析

創(chuàng)建右鍵菜單帅腌,便于切換各種模式

//創(chuàng)建菜單,綁定一個(gè)響應(yīng)方法
glutCreateMenu(ProcessMenu);
//為菜單添加選項(xiàng)
glutAddMenuEntry("Toggle depth test", 1);
glutAddMenuEntry("Toggle cull backface", 2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
//右鍵彈出菜單
glutAttachMenu(GLUT_RIGHT_BUTTON);

物體 3D 旋轉(zhuǎn)麻汰,便于查看各種模式優(yōu)劣

//上速客、下、左五鲫、右按鍵溺职,3D 旋轉(zhuǎn)
/*
 * RotateWorld(float fAngle, float x, float y, float z)
 * fAngle: 旋轉(zhuǎn)弧度, x/y/z:以哪個(gè)坐標(biāo)軸旋轉(zhuǎn)
 * m3dDegToRad:角度 -> 弧度
 * viewFrame:物體世界坐標(biāo)
 */
switch (key) {
    case GLUT_KEY_UP:
        viewFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
        break;
    case GLUT_KEY_DOWN:
        viewFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
        break;
    case GLUT_KEY_LEFT:
        viewFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
        break;
    case GLUT_KEY_RIGHT:
        viewFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
        break;
    default:
        break;
}

開(kāi)啟/關(guān)閉剔除

// 開(kāi)啟剔除
glEnable(GL_CULL_FACE);
// 關(guān)閉剔除
glDisable(GL_CULL_FACE);
// 默認(rèn)我們是剔除背面,也可以剔除正面位喂,調(diào)以下函數(shù)進(jìn)行控制
glCullFace(GL_FRONT); // 控制剔除的面為正面
glCullFace(GL_BACK); // 控制剔除的面為背面
glCullFace(GL_FRONT_AND_BACK); // 控制剔除的面為雙面

開(kāi)啟/關(guān)閉深度測(cè)試

//  開(kāi)啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
// 關(guān)閉深度測(cè)試
glDisable(GL_DEPTH_TEST);

配置多邊形模式

//多邊形面模式為雙面實(shí)體
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//多邊形面模式為雙面輪廓
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//多邊形面模式為雙面點(diǎn)
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);

全部源碼

#include <GLTools.h>    // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <math.h>
#include <glut/glut.h>

GLFrame             viewFrame;
GLFrustum           viewFrustum;
GLTriangleBatch     torusBatch;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager     shaderManager;

//是否開(kāi)啟正反面剔除
int iCull = 0;
//是否開(kāi)始深度測(cè)試
int iDepth = 0;

//點(diǎn)擊菜單選項(xiàng)
void ProcessMenu(int value) {
    switch(value) {
        case 1:
            //開(kāi)關(guān)深度測(cè)試
            iDepth = !iDepth;
            break;
        case 2:
            //開(kāi)關(guān)正反面剔除
            iCull = !iCull;
            break;
        case 3:
            //開(kāi)關(guān)多邊形面模式
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            break;
        case 4:
            //開(kāi)關(guān)多邊形線模式
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            break;
        case 5:
            //開(kāi)關(guān)多邊形點(diǎn)模式
            glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
            break;
    }
    
    //觸發(fā)渲染
    glutPostRedisplay();
}

//渲染畫(huà)面
void RenderScene(void) {
    //清除一個(gè)或一組特定的緩沖區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //判斷是否開(kāi)啟正反面剔除
    if(iCull)
        glEnable(GL_CULL_FACE);
    else
        glDisable(GL_CULL_FACE);

    //判斷是否開(kāi)啟深度測(cè)試
    if(iDepth)
        glEnable(GL_DEPTH_TEST);
    else
        glDisable(GL_DEPTH_TEST);
    
    //保存當(dāng)前的模型視圖矩陣
    modelViewMatrix.PushMatrix(viewFrame);
    
    //這里使用默認(rèn)光源進(jìn)行著色,可以看到陰影
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
    
    //畫(huà)花托
    torusBatch.Draw();
    
    // 還原以前的模型視圖矩陣
    modelViewMatrix.PopMatrix();
    
    //將在后臺(tái)緩沖區(qū)進(jìn)行渲染塑崖,然后在結(jié)束時(shí)交換到前臺(tái)
    glutSwapBuffers();
}

//為程序作一次性的設(shè)置
void SetupRC() {
    //設(shè)置窗口背景顏色
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
    
    //初始化著色器管理器
    shaderManager.InitializeStockShaders();
    
    //設(shè)置變換管線以使用兩個(gè)矩陣堆棧
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    //移動(dòng)物體的位置点待,值越大物體越遠(yuǎn),值越小物體越近
    viewFrame.MoveForward(6.0f);
    
    //創(chuàng)建一個(gè)花托批次弃舒,參數(shù)依次是:批次癞埠,外半徑,內(nèi)半徑聋呢,片段數(shù)苗踪,堆疊數(shù)(在計(jì)算機(jī)中圓是正多邊形,頂點(diǎn)數(shù)越多越像真正的圓)
    gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
    
    //設(shè)置默認(rèn)點(diǎn)大小
    glPointSize(4.0f);
}

//特殊按鍵(功能鍵或者方向鍵)監(jiān)聽(tīng)
void SpecialKeys(int key, int x, int y) {
    //上削锰、下通铲、左、右按鍵器贩,3D 旋轉(zhuǎn)
    /*
     * RotateWorld(float fAngle, float x, float y, float z)
     * fAngle: 旋轉(zhuǎn)弧度, x/y/z:以哪個(gè)坐標(biāo)軸旋轉(zhuǎn)
     * m3dDegToRad:角度 -> 弧度
     */
    switch (key) {
        case GLUT_KEY_UP:
            viewFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_DOWN:
            viewFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_LEFT:
            viewFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
            break;
        case GLUT_KEY_RIGHT:
            viewFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
            break;
        default:
            break;
    }
    
    //觸發(fā)渲染
    glutPostRedisplay();
}

//窗口大小改變時(shí)接受新的寬度和高度颅夺,其中0,0代表窗口中視口的左下角坐標(biāo)朋截,w,h代表像素
void ChangeSize(int width, int height) {
    // 防止下面除法的除數(shù)為0導(dǎo)致的閃退
    if(height == 0) height = 1;
    
    //設(shè)置視圖窗口位置
    glViewport(0, 0, width, height);
    
    // 創(chuàng)建投影矩陣吧黄,并將它載入到投影矩陣堆棧中
    viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
}

//程序入口
int main(int argc, char* argv[]) {
    //設(shè)置當(dāng)前工作目錄部服,針對(duì)MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT庫(kù)
    glutInit(&argc, argv);
    
    /*初始化渲染模式,其中標(biāo)志GLUT_DOUBLE拗慨、GLUT_RGBA廓八、GLUT_DEPTH、GLUT_STENCIL分別指
     雙緩沖窗口赵抢、RGBA顏色模式剧蹂、深度測(cè)試、模板緩沖區(qū)*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    //初始化窗口大小
    glutInitWindowSize(800, 600);
    //創(chuàng)建窗口
    glutCreateWindow("Geometry Test Program");
    
    //注冊(cè)回調(diào)函數(shù)
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    //創(chuàng)建菜單烦却,綁定一個(gè)響應(yīng)方法
    glutCreateMenu(ProcessMenu);
    
    //為菜單添加選項(xiàng)
    glutAddMenuEntry("Toggle depth test",1);
    glutAddMenuEntry("Toggle cull backface",2);
    glutAddMenuEntry("Set Fill Mode", 3);
    glutAddMenuEntry("Set Line Mode", 4);
    glutAddMenuEntry("Set Point Mode", 5);
    
    //右鍵彈出菜單
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    //確保驅(qū)動(dòng)程序的初始化中沒(méi)有出現(xiàn)任何問(wèn)題宠叼。
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }
    
    //初始化設(shè)置
    SetupRC();
    
    //進(jìn)入調(diào)用循環(huán)
    glutMainLoop();
    return 0;
}

Demo 源代碼在這里:github->openGLDemo->04-GeoTest

有什么問(wèn)題可以在下方評(píng)論區(qū)提出,寫(xiě)得不好可以提出你的意見(jiàn)其爵,我會(huì)合理采納的车吹,O(∩_∩)O哈哈~,求關(guān)注求贊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末醋闭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子朝卒,更是在濱河造成了極大的恐慌证逻,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抗斤,死亡現(xiàn)場(chǎng)離奇詭異囚企,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瑞眼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)龙宏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伤疙,你說(shuō)我怎么就攤上這事银酗。” “怎么了徒像?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵黍特,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我锯蛀,道長(zhǎng)灭衷,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任旁涤,我火速辦了婚禮翔曲,結(jié)果婚禮上迫像,老公的妹妹穿的比我還像新娘。我一直安慰自己瞳遍,他們只是感情好闻妓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著傅蹂,像睡著了一般纷闺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上份蝴,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天犁功,我揣著相機(jī)與錄音,去河邊找鬼婚夫。 笑死浸卦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的案糙。 我是一名探鬼主播限嫌,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼时捌!你這毒婦竟也來(lái)了怒医?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奢讨,失蹤者是張志新(化名)和其女友劉穎稚叹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拿诸,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扒袖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亩码。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片季率。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖描沟,靈堂內(nèi)的尸體忽然破棺而出飒泻,到底是詐尸還是另有隱情,我是刑警寧澤吏廉,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布蠢络,位于F島的核電站,受9級(jí)特大地震影響迟蜜,放射性物質(zhì)發(fā)生泄漏刹孔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望髓霞。 院中可真熱鬧卦睹,春花似錦、人聲如沸方库。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纵潦。三九已至徐鹤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邀层,已是汗流浹背返敬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寥院,地道東北人劲赠。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秸谢,于是被迫代替她去往敵國(guó)和親凛澎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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