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

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

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

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

之前我們都只是畫平面圖形,這節(jié)我們來畫一個(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, // 返回的三角形批次,和普通批次功能類似,是專門畫三角形的批次
    GLfloat majorRadius, // 中心到外邊緣的半徑
    GLfloat minorRadius, // 外邊緣到內(nèi)邊緣的距離
    GLint numMajor, // 片段數(shù),即花托由多少個(gè)片段面組成
    GLint numMinor  // 堆疊數(shù),即每個(gè)片段面由多少個(gè)頂點(diǎn)組成
); 

油畫法渲染

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

對(duì)于這些問題,可能的解決方法之一是,對(duì)這些三角形進(jìn)行排序,先渲染較遠(yuǎn)的三角形,再渲染較近的三角形,這種方式稱為“油畫法”?!居彤嫹ㄤ秩臼欠浅5托У模丿B部分進(jìn)行了多次寫操作,三角形排序開銷大】

正面和背面剔除

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

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

深度測(cè)試

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

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

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

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

開啟剔除,但未開啟深度測(cè)試
只開啟深度測(cè)試

多邊形模式

默認(rèn)情況下,多邊形是作為實(shí)心圖形進(jìn)行繪制,我們可以通過調(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;
}

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

// 開啟剔除
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); // 控制剔除的面為雙面

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

//  開啟深度測(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;

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

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

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

    //判斷是否開啟深度測(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);
    
    //畫花托
    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)聽
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庫
    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)程序的初始化中沒有出現(xià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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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