OpenGL 學習 06 多邊形偏移 裁剪 混合 抗鋸齒

執著丶執念發表於2018-06-02

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

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

書籍原始碼:OpenGL 超級寶典第五版原始碼 密碼:oyb4

環境搭建:OpenGL 學習 01 - Mac 搭建 OpenGL 環境

基本概念

多邊形偏移

上一節在 OpenGL 學習 05 - 花托 中,我們通過深度測試實現真實視覺並提高效能,但會遇到一些麻煩,比如我們想將2個幾何圖形繪製在同一個位置,我們想要畫一架大飛機,然後在飛機上繪製一個五角星圖案,這叫做“貼花”。這 2 個圖形的深度值 z 相同或者幾乎相同,這種情況稱為 深度值衝突

處理深度值衝突的方法:

  1. 手動調整 z 值進行一點點偏移,但可能會出現圖形懸浮(不推薦)

  2. 利用 多邊形偏移 調節片段的深度值,但實際不改變 3D 空間物理位置(推薦)

應用到片段上的總偏移方程式如下,其中 DZ 是深度值相對多邊形螢幕區域的變化量,r 是使深度緩衝區值產生變化的最小值,這2個值都是 OpenGL 內部的值,我們不用關心,我們是通過控制 factorunits 達到效果:

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

// 設定多邊形偏移
void glPolygonOffset(GLfloat factor, GLfloat units);
複製程式碼

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

裁剪

除了深度測試,還有一種提高渲染效能的方法,那就是 只重新整理螢幕上發生變化的部分,即裁剪

OpenGL 允許我們在將要進行渲染的視窗中指定一個裁剪框,只重新整理裁剪框裡面的變化。預設裁剪框和視窗大小一致,並不會進行裁剪測試。

//設定裁剪框位置和大小
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
複製程式碼

混合

通常情況下,OpenGL 渲染時會把顏色值放在顏色快取區中,任何繪製操作都是完全覆蓋原來的顏色值,比如我們在一個紅色圖形前面畫一個藍色圖形,在重疊的部分,藍色覆蓋紅色。

但如果我們想在重疊區顯示重疊顏色(比如紅藍混合色)怎麼辦?這時就需要使用到 OpenGL 的混合功能。

目標顏色:已經儲存在顏色緩衝區中的顏色

源顏色:將要加入進行混合的顏色

混合方程式:目標顏色和源顏色的組合方式,用來生成混合後的顏色

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

其中 Cf 是最終產生顏色,Cs 為源顏色,Cd 為目標顏色,SD 分別為源顏色和目標顏色的混合因子

// 我們通過控制 S 和 D 混合因子控制混合方程式輸出,S 和 D 都是列舉值
void glBlendFunc(CLenum S, GLenum D);
複製程式碼

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

看到上面的混合因子表,一開始是懵逼的,我們來簡單計算一下,到底表示的意義是什麼,下面是其中一種情況的計算過程,其他類推:

/*
Rs/Gs/Bs/As - 源顏色 RGBA 各個通道的混合因子
Rd/Gd/Bd/Ad - 目標顏色 RGBA 各個通道的混合因子
Rc/Gc/Bc/Ac - 常量顏色 RGBA 各個通道的混合因子
Cs = 源顏色 = { 0.0f, 0.0f, 1.0f, 0.6f } 
Cd = 目標顏色 = { 1.0f, 0.0f, 0.0f, 1.0f } 
As = 源顏色 alpha 值 = 0.6f
Ad = 目標顏色 alpha 值 = 1.0f
S = 源顏色混合因子 = GL_SRC_ALPHA = As = 0.6f
D = 目標顏色混合因子 = GL_ONE_MINUS_SRC_COLOR = 1.0f - As = 0.4f
Cf = 最終產生顏色 = Cs * 0.6f + Cd * 0.4f = {
   0.0f * 0.6f + 1.0f * 0.4f,
   0.0f * 0.6f + 0.0f * 0.4f,
   1.0f * 0.6f + 0.0f * 0.4f,
   0.6f * 0.6f + 1.0f * 0.4f
} = { 0.4f, 0.6f, 0.0f, 0.76f }
*/
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
複製程式碼

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

但是這會帶來一個問題,當我們進行了顏色混合後,最終目標顏色分量 Alpha 通道同時也被改變了——原來 Alpha 是 0.6,混合後變成了 0.76。這時我們就需要另外一個函式 glBlendFuncSeparate

//允許分別為 RGB 通道和 Alpha 通道設定混合因子(OpenGL 2.0 開始支援)
void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
複製程式碼

除了上面預設的混合方程式,也有其他混合方程式:

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

//改變混合方程式等式結構
void glBlendEquation(GLenum mode);
//修改常量顏色,即上面表中的 Rc、Gc、Bc、Ac
void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
複製程式碼

抗鋸齒

OpenGL 混合的另外一種用途就是抗鋸齒。圖形邊緣會出現一些吸引眼睛的注意力而讓人感覺圖形不自然的畫素點,稱為鋸齒。我們需要儘可能的逼真,尤其在遊戲、模擬和藝術創造中。為了消除圖元之間的鋸齒,OpenGL 利用混合功能,把畫素的目標顏色和周圍畫素的顏色進行混合。

抗鋸齒功能開啟條件:

  1. 開啟混合功能

  2. 設定混合方程式為 GL_ADD

  3. 設定混合方程式因子為 S = GL_SRC_ALPHA, D = GL_ONE_MINUS_SRC_ALPHA

  4. 開啟抗鋸齒功能(點/線/多邊形)

原始碼解析

多邊形偏移

核心程式碼:

//設定多邊形偏移的總偏移
glPolygonOffset(-1.0f, -1.0f);
//開啟多邊形偏移
glEnable(GL_POLYGON_OFFSET_POINT); // 點
glEnable(GL_POLYGON_OFFSET_LINE); // 線
glEnable(GL_POLYGON_OFFSET_UNITS); // 圖形
//關閉多邊形偏移
glDisable(GL_POLYGON_OFFSET_POINT); // 點
glDisable(GL_POLYGON_OFFSET_LINE); // 線
glDisable(GL_POLYGON_OFFSET_UNITS); // 圖形
複製程式碼

Demo 原始碼: 之前文章的 03-Primitives Demo 中的三角形帶中有運用到,這裡就不重複拷貝了。

裁剪

核心程式碼:

//開啟裁剪
glEnable(GL_SCISSOR_TEST);
//設定顏色緩衝區背景為紅色
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
//裁剪出(x: 100, y: 100, w: 600, h: 400)區域
glScissor(100, 100, 600, 400);
//清空顏色快取區,同步到後臺緩衝區,注意這裡會設定的是裁剪區域
glClear(GL_COLOR_BUFFER_BIT);
//關閉裁剪
glDisable(GL_SCISSOR_TEST);
複製程式碼

Demo 原始碼: 05-Scissor

#include <GLTools.h>        // OpenGL toolkit
#include <glut/glut.h>

//渲染畫面
void RenderScene(void) {
    
    //設定顏色緩衝區背景色為藍色
    glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
    //清空顏色快取區,設定到後臺快取區,執行完這步才繪製了背景顏色
    glClear(GL_COLOR_BUFFER_BIT);
    //開啟裁剪
    glEnable(GL_SCISSOR_TEST);
    
    //設定顏色緩衝區背景色為紅色
    glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
    //裁剪出(x: 100, y: 100, w: 600, h: 400)區域
    glScissor(100, 100, 600, 400);
    //清空顏色快取區,設定到後臺緩衝區,注意這裡會設定的是裁剪區域
    glClear(GL_COLOR_BUFFER_BIT);
    
    //設定顏色緩衝區背景色為綠色
    glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    //裁剪出(x: 200, y: 200, w: 400, h: 200)區域
    glScissor(200, 200, 400, 200);
    //清空顏色快取區,設定到後臺緩衝區,注意這裡會設定的是裁剪區域
    glClear(GL_COLOR_BUFFER_BIT);
    
    //關閉裁剪
    glDisable(GL_SCISSOR_TEST);
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
}

//視窗大小改變時接受新的寬度和高度
void ChangeSize(int w, int h) {
    //設定檢視視窗位置
    glViewport(0, 0, w, h);
}

//程式入口
int main(int argc, char* argv[]) {
    //設定當前工作目錄,針對MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT庫
    glutInit(&argc, argv);
    
    /*初始化渲染模式,其中標誌GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分別指
     雙緩衝視窗、RGBA顏色模式、深度測試、模板緩衝區*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    
    //初始化視窗大小
    glutInitWindowSize(800, 600);
    //建立視窗
    glutCreateWindow("OpenGL Scissor");
    
    //註冊回撥函式
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    
    //確保驅動程式的初始化中沒有出現任何問題。
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }

    //進入呼叫迴圈
    glutMainLoop();
    return 0;
}
複製程式碼

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

混合

核心原始碼:

//開啟顏色混合
glEnable(GL_BLEND);
//配置混合方程式,預設為 GL_FUNC_ADD 方程
glBlendEquation(GL_FUNC_ADD);
//配置混合方程式混合因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//分別設定 RGB 通道和 Alpha 通道混合因子
glBlendFuncSeparate(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//改變常量顏色
glBlendColor(1.0f, 0.0f, 0.0f, 1.0f);
//關閉顏色混合
glDisable(GL_BLEND);
複製程式碼

Demo 原始碼: 06-Blending

#include <GLTools.h>    // OpenGL toolkit
#include <GLShaderManager.h>
#include <glut/glut.h>

GLBatch squareBatch;
GLBatch greenBatch;
GLBatch redBatch;
GLBatch blueBatch;
GLBatch blackBatch;

GLShaderManager shaderManager;

GLfloat blockSize = 0.2f;
GLfloat vVerts[] = {
    -blockSize, -blockSize, 0.0f,
    blockSize, -blockSize, 0.0f,
    blockSize,  blockSize, 0.0f,
    -blockSize,  blockSize, 0.0f
};

//程式一次性初始化
void SetupRC() {
    //設定視窗背景為黑色
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f );
    
    //初始化著色器管理者
    shaderManager.InitializeStockShaders();
    
    //建立移動矩形批次
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
    
    //綠色矩形批次
    GLfloat vBlock[] = {
        0.25f, 0.25f, 0.0f,
        0.75f, 0.25f, 0.0f,
        0.75f, 0.75f, 0.0f,
        0.25f, 0.75f, 0.0f
    };
    greenBatch.Begin(GL_TRIANGLE_FAN, 4);
    greenBatch.CopyVertexData3f(vBlock);
    greenBatch.End();
    
    //紅色矩形批次
    GLfloat vBlock2[] = {
        -0.75f, 0.25f, 0.0f,
        -0.25f, 0.25f, 0.0f,
        -0.25f, 0.75f, 0.0f,
        -0.75f, 0.75f, 0.0f
    };
    redBatch.Begin(GL_TRIANGLE_FAN, 4);
    redBatch.CopyVertexData3f(vBlock2);
    redBatch.End();
    
    //藍色矩形批次
    GLfloat vBlock3[] = {
        -0.75f, -0.75f, 0.0f,
        -0.25f, -0.75f, 0.0f,
        -0.25f, -0.25f, 0.0f,
        -0.75f, -0.25f, 0.0f
    };
    blueBatch.Begin(GL_TRIANGLE_FAN, 4);
    blueBatch.CopyVertexData3f(vBlock3);
    blueBatch.End();
    
    //黑色矩形批次
    GLfloat vBlock4[] = {
        0.25f, -0.75f, 0.0f,
        0.75f, -0.75f, 0.0f,
        0.75f, -0.25f, 0.0f,
        0.25f, -0.25f, 0.0f
    };
    blackBatch.Begin(GL_TRIANGLE_FAN, 4);
    blackBatch.CopyVertexData3f(vBlock4);
    blackBatch.End();
}

//特殊按鈕監聽
void SpecialKeys(int key, int x, int y) {
    GLfloat stepSize = 0.025f;
    
    //左上角的 X 座標和右下角的 Y 座標
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[7];
    
    //根據移動方向移動位置
    switch (key) {
        case GLUT_KEY_UP: blockY += stepSize; break;
        case GLUT_KEY_DOWN: blockY -= stepSize; break;
        case GLUT_KEY_LEFT: blockX -= stepSize; break;
        case GLUT_KEY_RIGHT: blockX += stepSize; break;
        default: break;
    }
    
    //移動邊界處理
    if(blockX < -1.0f) blockX = -1.0f;
    if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;;
    if(blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;
    if(blockY > 1.0f) blockY = 1.0f;
    
    //矩形四個頂點位置
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize*2;
    vVerts[3] = blockX + blockSize*2;
    vVerts[4] = blockY - blockSize*2;
    vVerts[6] = blockX + blockSize*2;
    vVerts[7] = blockY;
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    //批次頂點資料編號
    squareBatch.CopyVertexData3f(vVerts);
    
    //觸發渲染
    glutPostRedisplay();
}

//渲染畫面
void RenderScene(void) {
    //清理各個快取區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //定義4種顏色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 0.6f };
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    
    //畫綠色矩形
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
    greenBatch.Draw();
    
    //畫紅色矩形
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    redBatch.Draw();
    
    //畫藍色矩形
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    blueBatch.Draw();
    
    //畫黑色矩形
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
    blackBatch.Draw();
    
    //開啟顏色混合
    glEnable(GL_BLEND);
    //配置混合方程式
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //畫移動矩形,本身半透明藍色
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    squareBatch.Draw();
    //關閉顏色混合
    glDisable(GL_BLEND);
    
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
}

//視窗大小改變時接受新的寬度和高度
void ChangeSize(int w, int h) {
    
    //設定檢視視窗位置
    glViewport(0, 0, w, h);
}

//程式入口
int main(int argc, char* argv[]) {
    //設定當前工作目錄,針對MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT庫
    glutInit(&argc, argv);
    
    /*初始化渲染模式,其中標誌GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分別指
     雙緩衝視窗、RGBA顏色模式、深度測試、模板緩衝區*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    
    //初始化視窗大小
    glutInitWindowSize(800, 600);
    //建立視窗
    glutCreateWindow("Move Block with Arrow Keys to see blending");
    
    //註冊回撥函式
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    //確保驅動程式的初始化中沒有出現任何問題。
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }
    
    //初始化設定
    SetupRC();
    
    //進入呼叫迴圈
    glutMainLoop();
    return 0;
}
複製程式碼

OpenGL 學習 06   多邊形偏移 裁剪 混合 抗鋸齒

抗鋸齒

核心程式碼:

//開啟抗鋸齒處理,必須先開啟顏色混合模式
glBlendEquation(GL_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
//抗鋸齒開啟
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
//設定抗鋸齒處理達到效果最好(另外一個是效果最快)
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
//關閉抗鋸齒處理
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
複製程式碼

Demo 原始碼: 07-Smoother

#include <GLTools.h>    // OpenGL toolkit
#include <GLFrustum.h>
#include <glut/glut.h>

GLShaderManager shaderManager;
GLFrustum viewFrustum;
GLBatch smallStarBatch;
GLBatch mediumStarBatch;
GLBatch largeStarBatch;
GLBatch mountainRangeBatch;
GLBatch moonBatch;

//常量巨集
#define SMALL_STARS     100
#define MEDIUM_STARS     40
#define LARGE_STARS      15

#define SCREEN_X        800
#define SCREEN_Y        600

//點選選單選項觸發的回撥方法
void ProcessMenu(int value) {
    switch(value) {
        case 1:
            //開啟抗鋸齒處理,必須先開啟顏色混合模式
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glEnable(GL_BLEND);
            //點抗鋸齒
            glEnable(GL_POINT_SMOOTH);
            glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
            //線抗鋸齒
            glEnable(GL_LINE_SMOOTH);
            glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
            //多邊形抗鋸齒
            glEnable(GL_POLYGON_SMOOTH);
            glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
            break;
        case 2:
            //關閉抗鋸齒處理
            glDisable(GL_BLEND);
            glDisable(GL_LINE_SMOOTH);
            glDisable(GL_POINT_SMOOTH);
            glDisable(GL_POLYGON_SMOOTH);
            break;
        default:
            break;
    }
    
    //觸發渲染
    glutPostRedisplay();
}

//渲染畫面
void RenderScene(void) {
    //清除快取區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //設定著色器為單位黑色
    GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_FLAT, viewFrustum.GetProjectionMatrix(), vWhite);
    
    //畫小點
    glPointSize(1.0f);
    smallStarBatch.Draw();
    
    //畫大點
    glPointSize(4.0f);
    mediumStarBatch.Draw();
    
    //畫超大點
    glPointSize(8.0f);
    largeStarBatch.Draw();
    
    //畫月亮
    moonBatch.Draw();
    
    //畫山的輪廓
    glLineWidth(3.5);
    mountainRangeBatch.Draw();
    
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
}

//初始化小點批次
void SetupSmallStarBatch() {
    M3DVector3f vVerts[SMALL_STARS];
    for(int i = 0; i < SMALL_STARS; i++) {
        vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
        vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
        vVerts[i][2] = 0.0f;
    }
    smallStarBatch.Begin(GL_POINTS, SMALL_STARS);
    smallStarBatch.CopyVertexData3f(vVerts);
    smallStarBatch.End();
}

//初始化大點批次
void SetupMeiumStarBatch() {
    M3DVector3f vVerts[MEDIUM_STARS];
    for(int i = 0; i < MEDIUM_STARS; i++) {
        vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
        vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
        vVerts[i][2] = 0.0f;
    }

    mediumStarBatch.Begin(GL_POINTS, MEDIUM_STARS);
    mediumStarBatch.CopyVertexData3f(vVerts);
    mediumStarBatch.End();
}

//初始化超大點批次
void SetupLargeStarBatch() {
    M3DVector3f vVerts[LARGE_STARS];
    for(int i = 0; i < LARGE_STARS; i++) {
        vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
        vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
        vVerts[i][2] = 0.0f;
    }
    
    largeStarBatch.Begin(GL_POINTS, LARGE_STARS);
    largeStarBatch.CopyVertexData3f(vVerts);
    largeStarBatch.End();
}

//初始化山輪廓批次
void SetupMountainRangeBatch() {
    M3DVector3f vMountains[12] = {
        0.0f, 25.0f, 0.0f,
        50.0f, 100.0f, 0.0f,
        100.0f, 25.0f, 0.0f,
        225.0f, 125.0f, 0.0f,
        300.0f, 50.0f, 0.0f,
        375.0f, 100.0f, 0.0f,
        460.0f, 25.0f, 0.0f,
        525.0f, 100.0f, 0.0f,
        600.0f, 20.0f, 0.0f,
        675.0f, 70.0f, 0.0f,
        750.0f, 25.0f, 0.0f,
        800.0f, 90.0f, 0.0f
    };
    
    mountainRangeBatch.Begin(GL_LINE_STRIP, 12);
    mountainRangeBatch.CopyVertexData3f(vMountains);
    mountainRangeBatch.End();
}

//初始化月亮批次
void SetupMoonBatch() {
    GLfloat x = 700.0f;
    GLfloat y = 500.0f;
    GLfloat r = 50.0f;
    M3DVector3f vVerts[SMALL_STARS];
    int nVerts = 0;
    vVerts[nVerts][0] = x;
    vVerts[nVerts][1] = y;
    vVerts[nVerts][2] = 0.0f;
    for(GLfloat angle = 0; angle < M3D_2PI; angle += 0.2f) {
        nVerts++;
        vVerts[nVerts][0] = x + float(cos(angle)) * r;
        vVerts[nVerts][1] = y + float(sin(angle)) * r;
        vVerts[nVerts][2] = 0.0f;
    }
    nVerts++;
    vVerts[nVerts][0] = x + r;;
    vVerts[nVerts][1] = y;
    vVerts[nVerts][2] = 0.0f;
    
    moonBatch.Begin(GL_TRIANGLE_FAN, 34);
    moonBatch.CopyVertexData3f(vVerts);
    moonBatch.End();
}

//程式化一次性初始化
void SetupRC() {
    //設定背景色為黑色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
    
    //初始化著色器
    shaderManager.InitializeStockShaders();
    
    //初始化各個圖元批次
    SetupSmallStarBatch();
    SetupMeiumStarBatch();
    SetupLargeStarBatch();
    SetupMountainRangeBatch();
    SetupMoonBatch();
}

//視窗大小改變時接受新的寬度和高度
void ChangeSize(int w, int h) {
    glViewport(0, 0, w, h);
    
    //正投影
    viewFrustum.SetOrthographic(0.0f, SCREEN_X, 0.0f, SCREEN_Y, -1.0f, 1.0f);
}

//程式入口
int main(int argc, char* argv[]) {
    //設定當前工作目錄,針對MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //GLUT初始化
    glutInit(&argc, argv);
    
    /*初始化渲染模式,其中標誌GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分別指
     雙緩衝視窗、RGBA顏色模式、深度測試、模板緩衝區*/
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    
    //建立視窗大小、標題
    glutInitWindowSize(800, 600);
    glutCreateWindow("Smoothing Out The Jaggies");
    
    //建立選單並繫結回撥函式,新增選項,確定右鍵觸發
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Antialiased Rendering",1);
    glutAddMenuEntry("Normal Rendering",2);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    //註冊回撥函式
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    
    //判斷驅動是否正常
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    //初始化
    SetupRC();
    
    //執行迴圈
    glutMainLoop();
    return 0;
}
複製程式碼

開啟抗鋸齒前效果

開啟抗鋸齒後效果

上面的 Demo 原始碼全部都放在我的 github/OpenGLDemo 上,大家可以去下載和除錯。

有什麼問題可以在下方評論區提出,寫得不好可以提出你的意見,我會合理採納的,O(∩_∩)O哈哈~,求關注求贊

相關文章