OpenGL-09-繪制甜甜圈及正背面剔除

我們通過(guò)現(xiàn)成API實(shí)現(xiàn)一個(gè)甜甜圈的圖案和悦,并且使用默認(rèn)光源著色器進(jìn)行存儲(chǔ)初橘,通過(guò)轉(zhuǎn)動(dòng)觀察者視角奥额,但物體本身不動(dòng)的方式來(lái)看一下繪制的圖案所出現(xiàn)的bug

慣例愉昆,放一下流程圖(代碼實(shí)現(xiàn)直接放在文章末尾):


image.png

效果圖:


image.gif

黑色部分是什么薄疚?如何解決碧信?

黑色部分其實(shí)是隱藏面

1、油畫算法

油畫算法街夭,我們之前的文章介紹過(guò):先繪制場(chǎng)景中的離觀察者較遠(yuǎn)的物體,再繪制較近的物體砰碴。

那么,這種?法在計(jì)算機(jī)圖形處理中是?常低效的板丽。
1呈枉、我們必須對(duì)任何發(fā)??何圖形重疊的地?每個(gè)像素進(jìn)?2 次寫操作,?在存儲(chǔ)其中進(jìn)?寫操作會(huì)使速度變慢
2埃碱、對(duì)獨(dú)?三?形進(jìn)?排序的開(kāi)銷過(guò)?
3猖辫、油畫算法 有瓶頸期. ?如繪制圖像交疊時(shí), 沒(méi)有明確的先后順序就?從下?繪制了
如果有多個(gè)三?形疊加在一起的情況,油畫算法將?法處理.

2、正背面剔除法(Face Culling)

背景:
首先砚殿,我們觀察一個(gè)3D物體啃憎,不管從任何?個(gè)?向去觀察,最多可以看到?個(gè)??
答案是:最多3?似炎。從?個(gè)??體的任意位置和?向上看辛萍,你不可能看到多于3個(gè)?.。
那么羡藐, 我們?yōu)楹我嘤嗟娜ダL制那根本看不到的3個(gè)??
如果我們能以某種?式去丟棄這部分?jǐn)?shù)據(jù),OpenGL 在渲染的性能即可提?超過(guò)50%.

  • 如何知道某個(gè)?在觀察者的視野中不會(huì)出現(xiàn)?
    任何平?都有2個(gè)?,正?/背?.意味著你?個(gè)時(shí)刻只能看到??贩毕。

  • OpenGL 可以做到檢查所有正?朝向觀察者的?,并渲染它們.從?丟棄背?朝向的?. 這樣可以節(jié)約?元著?器的性能。

  • 那如何告訴OpenGL仆嗦,我們繪制的圖形 哪個(gè)是正面哪個(gè)是反面辉阶?
    通過(guò)分析頂點(diǎn)數(shù)據(jù)的順序就能知道(正面:逆時(shí)針 反面:順時(shí)針)

  • 對(duì)于正?、背?三?形進(jìn)?區(qū)分的原因之?欧啤,就是為了進(jìn)?剔除睛藻。

因此,我們可以給平?定義正?和背?,OpenGL可以做到檢查所有正?朝向觀察者的?邢隧,并渲染它們店印,從?丟棄 背?朝向的?。OpenGL渲染的性能即可提?超過(guò)50%

原理:
我們不去繪制那些根本看不到的?倒慧,以某種?式去丟棄這部分?jǐn)?shù)據(jù)

API:

    //開(kāi)啟
    glEnable(GL_CULL_FACE);
    //關(guān)閉
    glDisable(GL_CULL_FACE);
    //設(shè)置哪一面為正面, GL_CW 按摘、GL_CCW ,默認(rèn)GL_CCW
    glFrontFace(GL_CCW);
    //設(shè)置剔除哪一面, GL_FRONT 包券、 GL_BACK 、GL_FRONT_AND_BACK ,默認(rèn)GL_BACK
    glCullFace(GL_BACK);
    
    /*
     思考:默認(rèn)剔除背面炫贤,那么我們想剔除正面該怎么做呢溅固?
     第一種:
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
     第二種:
     glCullFace(GL_FRONT);
     */

那么通過(guò)正背面剔除的技巧,把不顯示的背面剔除之后兰珍,我們來(lái)看一下效果圖:


image.gif

新的問(wèn)題又來(lái)了:再轉(zhuǎn)動(dòng)過(guò)程中侍郭,會(huì)發(fā)現(xiàn)有的地方感覺(jué)像是被吃了一口似的。是什么原因呢掠河?又如何解決呢亮元?

原因:其實(shí)是兩個(gè)正面疊加在一起,出現(xiàn)了混合唠摹, 此時(shí)OpenGL不能清楚分辨 哪個(gè)圖層在前 哪個(gè)圖層在后爆捞,于是就會(huì)出現(xiàn)甜甜圈像被啃??的現(xiàn)象

深度測(cè)試

深度緩沖區(qū)(DepthBuffer)和顏?緩存區(qū)(ColorBuffer)是對(duì)應(yīng)的.顏?緩存區(qū)存儲(chǔ)像素的顏?信
息,?深度緩沖區(qū)存儲(chǔ)像素的深度信息. 在決定是否繪制?個(gè)物體表?時(shí), ?先要將表?對(duì)應(yīng)的像
素的深度值與當(dāng)前深度緩沖區(qū)中的值進(jìn)??較. 如果?于深度緩沖區(qū)中的值,則丟棄這部分.否則
利?這個(gè)像素對(duì)應(yīng)的深度值和顏?值.分別更新深度緩沖區(qū)和顏?緩存區(qū). 這個(gè)過(guò)程稱為”深度測(cè)
試”

API:

    //開(kāi)啟
    glEnable(GL_DEPTH_TEST);
    //關(guān)閉
    glDisable(GL_DEPTH_TEST);

最后我們?cè)倏聪麓蜷_(kāi)了深度測(cè)試的效果:


image.gif

哇,完美解決了勾拉!

思考與拓展

我們使用了默認(rèn)光源著色器煮甥,出現(xiàn)了隱藏面。那么如果使用平面著色器藕赞,還會(huì)出現(xiàn)隱藏面嗎成肘?

  • 答案是:會(huì)出現(xiàn),由于都是紅色的找默,沒(méi)有辦法區(qū)別誰(shuí)是正面艇劫,誰(shuí)是背面,所以導(dǎo)致肉眼無(wú)法察覺(jué)惩激。而在光源下店煞,被照射的部分呈現(xiàn)紅色,無(wú)法照射的部分會(huì)呈現(xiàn)黑色风钻,可以非常直觀的通過(guò)肉眼看出誰(shuí)是正面顷蟀,誰(shuí)是反面。

上面的動(dòng)圖可以看出骡技,右鍵菜單欄有改變狀態(tài)的選項(xiàng)鸣个,我們先看下一效果圖:


image
    //修改填充方式
    //充滿
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    //線
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //點(diǎn)
    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);

修改填充方式之后,也會(huì)出現(xiàn)隱藏面消除的問(wèn)題布朦,只是肉眼觀察不到囤萤。

源碼來(lái)啦!

#include "GLTools.h"
//矩陣
#include "GLMatrixStack.h"
//位置
#include "GLFrame.h"
//投影方式
#include "GLFrustum.h"
//變換管道是趴,快速傳輸矩陣
#include "GLGeometryTransform.h"
#include <math.h>

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

//觀察者視角
GLFrame viewFrame;
//投影方式
GLFrustum viewFrustum;
//容器類
GLTriangleBatch torusBatch;
//模型視圖矩陣
GLMatrixStack modelViewMatrix;
//投影矩陣
GLMatrixStack projectionMatrix;

GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;

//記錄正背面剔除
int iCull = 0;
//記錄深度測(cè)試
int iDepth = 0;



//窗口變化時(shí)候的回調(diào)函數(shù)
void changeSize(int w,int h)
{
    //防止寬高比中除數(shù)為0的bug
    if(h==0) h=1;
    
    //1涛舍、創(chuàng)建窗口
    glViewport(0, 0, w, h);
    //2、創(chuàng)建投影矩陣->透視投影(角度唆途、寬高比富雅、最小距離掸驱、最大距離)
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
    //3、拿到這個(gè)投影矩陣没佑,把它加載到我們聲明的透視矩陣
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //4毕贼、在我們的視圖模型矩陣 頂部載入單元矩陣,相當(dāng)于初始化蛤奢。(**注意**  這個(gè)默認(rèn)是有的鬼癣。可以不寫 )
    modelViewMatrix.LoadIdentity();
    //5啤贩、設(shè)置變換管道
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
}

//渲染場(chǎng)景
void RenderScene()
{
    //1扣溺、清除緩沖區(qū)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //2、判斷是否開(kāi)啟正背面剔除
    if (iCull) {
        glEnable(GL_CULL_FACE);
        //擴(kuò)展
        //設(shè)置哪一面為正面, GL_CW 瓜晤、GL_CCW ,默認(rèn)GL_CCW
        glFrontFace(GL_CCW);
        //設(shè)置剔除哪一面, GL_FRONT 、 GL_BACK 腹纳、GL_FRONT_AND_BACK ,默認(rèn)GL_BACK
        glCullFace(GL_BACK);
        
        /*
         默認(rèn)剔除背面痢掠,那么我們想剔除正面該怎么做呢?
         第一種:
         glCullFace(GL_BACK);
         glFrontFace(GL_CW);
         第二種:
         glCullFace(GL_FRONT);
         */
        
    }else{
        glDisable(GL_CULL_FACE);
    }
    
    //3嘲恍、是否開(kāi)啟深度測(cè)試
    if(iDepth){
        glEnable(GL_DEPTH_TEST);
    }else{
        glDisable(GL_DEPTH_TEST);
    }
    
    
    //4足画、壓棧
    modelViewMatrix.PushMatrix(viewFrame);
    
    //5、使用默認(rèn)光源著色器
    GLfloat vColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vColor);
    
    //6佃牛、繪制
    torusBatch.Draw();
    
    //7淹辞、出棧
    modelViewMatrix.PopMatrix();
    
    //8、提交緩沖區(qū)
    glutSwapBuffers();
}


void SetupRC(){
    
    //1俘侠、背景色
    glClearColor(0.1, 0.1, 0.1, 1.0);
    //2象缀、初始化著色器
    shaderManager.InitializeStockShaders();
    //3、設(shè)置觀察者位置
    viewFrame.MoveForward(10.0f);
    //4爷速、繪制甜甜圈
    /*
     gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)
     1央星、GLTriangleBatch容器類
     2、外邊緣半徑
     3惫东、內(nèi)邊緣半徑
     4莉给、主半徑細(xì)分單元數(shù)量
     5、從半徑細(xì)分單元數(shù)量
     */
    gltMakeTorus(torusBatch, 1.0f, 0.3f, 60, 30);
    
    //切換點(diǎn)狀時(shí)點(diǎn)太小廉沮,這里提前設(shè)置
    glPointSize(5.0f);
}
//控制Camera的移動(dòng)颓遏,從而改變視口
void SpecialKeys(int key, int x, int y)
{
    //1.判斷方向
    if(key == GLUT_KEY_UP)
        //2.根據(jù)方向調(diào)整觀察者位置
        viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_DOWN)
        viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_LEFT)
        viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
    
    if(key == GLUT_KEY_RIGHT)
        viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
    
    //3.重新刷新
    glutPostRedisplay();
}


void RightButtonMenu(int value){
    
    switch(value)
    {
        case 1:
            iDepth = !iDepth;
            break;
            
        case 2:
            iCull = !iCull;
            break;
            
        case 3:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            break;
            
        case 4:
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            break;
            
        case 5:
            glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
            break;
    }
    
    glutPostRedisplay();
    
}
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("Laps");
    //函數(shù)回調(diào)
    glutReshapeFunc(changeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    //**新方法:右鍵打開(kāi)列表函數(shù)
    glutCreateMenu(RightButtonMenu);
    
    glutAddMenuEntry("深度測(cè)試",1);
    glutAddMenuEntry("正背面剔除",2);
    glutAddMenuEntry("充滿狀", 3);
    glutAddMenuEntry("線狀", 4);
    glutAddMenuEntry("點(diǎn)狀", 5);
    
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    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閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遥皂,死亡現(xiàn)場(chǎng)離奇詭異力喷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)演训,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門弟孟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人样悟,你說(shuō)我怎么就攤上這事拂募。” “怎么了窟她?”我有些...
    開(kāi)封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵陈症,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我震糖,道長(zhǎng)录肯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任吊说,我火速辦了婚禮论咏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颁井。我一直安慰自己厅贪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布雅宾。 她就那樣靜靜地躺著养涮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眉抬。 梳的紋絲不亂的頭發(fā)上贯吓,一...
    開(kāi)封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音吐辙,去河邊找鬼宣决。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昏苏,可吹牛的內(nèi)容都是我干的尊沸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贤惯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洼专!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起孵构,我...
    開(kāi)封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屁商,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后颈墅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜡镶,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雾袱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了官还。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芹橡。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖望伦,靈堂內(nèi)的尸體忽然破棺而出林说,到底是詐尸還是另有隱情,我是刑警寧澤屯伞,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布腿箩,位于F島的核電站,受9級(jí)特大地震影響劣摇,放射性物質(zhì)發(fā)生泄漏珠移。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一末融、第九天 我趴在偏房一處隱蔽的房頂上張望剑梳。 院中可真熱鬧,春花似錦滑潘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至酪刀,卻和暖如春粹舵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骂倘。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工眼滤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人历涝。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓诅需,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親荧库。 傳聞我的和親對(duì)象是個(gè)殘疾皇子堰塌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354