OpenGL 學習 04 圖元

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

OpenGL 學習 04   圖元

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

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

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

基本概念

著色器

OpenGL 著色器傳遞渲染資料方法:

  • 屬性值:對頂點資料作改變的資料元素
  • Uniform 值:系統定義的統一列舉值
  • 紋理:圖片檔案等大型資料塊

屬性值型別:

  • XYZ 頂點座標 -> GLT_ATTRIBUTE_VERTEX
  • RGBA 顏色值 -> GLT_ATTRIBUTE_COLOR
  • XYZ 表面法線 -> GLT_ATTRIBUTE_NORMAL
  • 第一對 ST 紋理座標 -> GLT_ATTRIBUTE_TEXTURE0
  • 第二對 ST 紋理座標 -> GLT_ATTRIBUTE_TEXTURE1

Uniform 值:

  • 單位著色器 -> GLT_SHADER_IDENTITY 引數:基本色
  • 平面著色器 -> GLT_SHADER_FLAT 引數:模型矩陣 + 基本色
  • 上色著色器 -> GLT_SHADER_SHADED 引數:模型矩陣
  • 預設光源著色器 -> GLT_SHADER_DEFAULT_LIGHT 引數:模型矩陣 + 投影矩陣 + 基本色
  • 點光源著色器 -> GLT_SHADER_POINT_LIGHT_DIFF 引數:模型矩陣 + 投影矩陣 + 光源位置 + 基本色
  • 紋理替換著色器 -> GLT_SHADER_TEXTURE_REPLACE 引數:模型矩陣 + 紋理單元
  • 紋理調整著色器 -> GLT_SHADER_TEXTURE_MODULATE 引數:模型矩陣 + 基本色 + 紋理單元
  • 紋理光源著色器 -> GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF 引數:模型矩陣 + 投影矩陣 + 光源位置 + 基本色 + 紋理單元
// 定義著色器管理器
GLShaderManager shaderManager;
// 初始化著色器管理器
shaderManager.InitializeStockShaders();
// 使用著色器
shaderManager.UseStockShader(引數...);
複製程式碼

座標系

正投影:在正投影中,所有在這個空間範圍內的所有東西都會被顯示在螢幕上,看不出遠近的區別

OpenGL 學習 04   圖元

透視投影:透視投影會進行透視除法對距離觀察者很遠的物件進行縮短和收縮,也就是離視點越遠,在視點看來物體越小,能看出遠近的區別

OpenGL 學習 04   圖元

圖元

OpenGL 系統定義的 7 種圖元型別:

  • 點 -> GL_POINTS:螢幕上單獨的點
  • 線段 -> GL_LINES:每對頂點定義一條線段
  • 線條 -> GL_LINE_STRIP:從起始點依次經過所有後續點的線條
  • 閉合線條 -> GL_LINE_LOOP:起始點和終點相連的線條
  • 三角形 -> GL_TRIANGLES:每 3 個頂點定義一個三角形
  • 三角形條帶 -> GL_TRIANGLE_STRIP:共用一個條帶上頂點的一組三角形
  • 三角形扇 -> GL_TRIANGLE_FAN:以圓點為中心呈扇形的共用相鄰頂點的一組三角形
// 批次初始化
GLBatch batch;
batch.Begin(圖元型別, 頂點數);
batch.CopyVertexData3f(頂點資料);
batch.End();
複製程式碼

環繞

環繞即各個點連線的順序,在預設情況下,OpenGL 認為具有逆時針方向環繞的多邊形是正面,因為我們會需要為多邊形的正面和背面設定不同的物理特徵。

OpenGL 學習 04   圖元

原始碼解析

改變點大小

// 預設情況下,點大小和其他圖形不同,並不會受到透視除法影響
void glPointSize(GLfloat size);
複製程式碼

獲取支援的點大小範圍和步長(增量)

GLfloat sizes[2];
GLfloat step;
// 獲取支援的點大小範圍
glGetFloatv(GL_POINT_SIZE_RANGE, sizes);
// 獲取支援的點步長(增量)
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);
複製程式碼

改變線寬度

void glLineWidth(GLfloat width);
複製程式碼

改變預設正面規則

/* 
 * GL_CW - 環繞順時針為正面,
 * GL_CCW - 環繞逆時針為正面
 */
glFrontFace(GL_CW);
複製程式碼

監聽普通按鍵點選事件

// 自定義普通按鍵點選監聽
void KeyPressFunc(unsigned char key, int x, int y) {
    // 處理
}
// 註冊普通按鍵點選回撥
glutKeyboardFunc(KeyPressFunc);
複製程式碼

綜合原始碼分析

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

GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
GLFrustum viewFrustum;

//各種圖元的批次
GLBatch pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;

//變換管線
GLGeometryTransform transformPipeline;

GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat vCoast[24][3] = {
    {2.80, 1.20, 0.0 }, {2.0,  1.20, 0.0 },
    {2.0,  1.08, 0.0 },  {2.0,  1.08, 0.0 },
    {0.0,  0.80, 0.0 },  {-.32, 0.40, 0.0 },
    {-.48, 0.2, 0.0 },   {-.40, 0.0, 0.0 },
    {-.60, -.40, 0.0 },  {-.80, -.80, 0.0 },
    {-.80, -1.4, 0.0 },  {-.40, -1.60, 0.0 },
    {0.0, -1.20, 0.0 },  { .2, -.80, 0.0 },
    {.48, -.40, 0.0 },   {.52, -.20, 0.0 },
    {.48,  .20, 0.0 },   {.80,  .40, 0.0 },
    {1.20, .80, 0.0 },   {1.60, .60, 0.0 },
    {2.0, .60, 0.0 },    {2.2, .80, 0.0 },
    {2.40, 1.0, 0.0 },   {2.80, 1.0, 0.0 }
};

int nStep = 0;

//初始化點批次
void SetupPointBatch() {
    pointBatch.Begin(GL_POINTS, 24);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
}
//初始化線批次
void SetupLineBatch() {
    lineBatch.Begin(GL_LINES, 24);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
}
//初始化線條批次
void SetupLineStripBatch() {
    lineStripBatch.Begin(GL_LINE_STRIP, 24);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
}
//初始化閉合線條批次
void SetupLineLoopBatch() {
    lineLoopBatch.Begin(GL_LINE_LOOP, 24);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();
}
//初始化三角形批次
void SetupTriangleBatch() {
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f
    };
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
}

//初始化三角形帶批次
void SetupTriangleStripBatch() {
    // 用程式碼生成三角形帶的頂點位置
    // x,y,z 座標的點
    GLfloat vPoints[100][3];
    int iCounter = 0;
    GLfloat radius = 3.0f;
    GLfloat height = 1.0f;
    for(GLfloat angle = 0.0f; angle <= M3D_2PI; angle += 0.3f) {
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0;
        iCounter++;
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = height;
        iCounter++;
    }
    
    //使三角形帶閉合
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = 0;
    iCounter++;
    
    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = height;
    iCounter++;
    
    //三角形帶批次初始化
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}

//初始化三角形扇批次
void SetupTriangleFanBatch() {
    // 用程式碼生成三角形扇的頂點位置
    // x,y,z 座標的點
    GLfloat vPoints[100][3];
    int nVerts = 0;
    GLfloat r = 3.0f;
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    
    for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
        nVerts++;
        vPoints[nVerts][0] = float(cos(angle)) * r;
        vPoints[nVerts][1] = float(sin(angle)) * r;
        vPoints[nVerts][2] = -r;
    }
    
    //使三角形扇閉合
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;
    
    //三角形扇批次初始化
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
}

//為程式作一次性的設定
void SetupRC() {
    //設定視窗背景顏色
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
    
    //初始化著色器管理器
    shaderManager.InitializeStockShaders();
    
    //開啟深度測試
    glEnable(GL_DEPTH_TEST);
    
    //設定變換管線以使用兩個矩陣堆疊
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    //移動攝像機的位置
    cameraFrame.MoveForward(-15.0f);
    
    //準備畫圖要用的批次
    SetupPointBatch();
    SetupLineBatch();
    SetupLineStripBatch();
    SetupLineLoopBatch();
    SetupTriangleBatch();
    SetupTriangleFanBatch();
    SetupTriangleStripBatch();
}

//畫點
void DrawPointBatch(GLBatch* pBatch) {
    //使用著色器
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    //設定點大小
    glPointSize(4.0f);
    //畫點
    pBatch->Draw();
    
    //還原繪畫環境
    glPointSize(1.0f);
}

//畫線
void DrawLineBatch(GLBatch* pBatch) {
    //使用著色器
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    //設定線粗
    glLineWidth(2.0f);
    //畫線
    pBatch->Draw();
    
    //還原繪畫環境
    glLineWidth(1.0f);
}

//畫三角形
void DrawTriangleBatch(GLBatch* pBatch) {
    //畫綠色面
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();
    
    //開啟調節片段的深度值,使深度值產生偏移而不實際改變 3D 空間的物理位置
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);
    
    //開啟線條的抗鋸齒
    glEnable(GL_LINE_SMOOTH);
    
    //開啟顏色混合
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //多邊形模式切換為前後面的線模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    //畫邊界黑線
    glLineWidth(2.5f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();
    
    //還原繪畫環境
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}

//渲染畫面
void RenderScene(void) {
    //清除一個或一組特定的緩衝區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //儲存當前的模型檢視矩陣 (單位矩陣)
    modelViewMatrix.PushMatrix();
    
    //MultMatrix: 用一個矩陣乘以矩陣堆疊的頂部矩陣,相乘得到的結果隨後將儲存在堆疊的頂部
    //處理模型相對於攝像機的位置
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);

    //處理模型自身的旋轉
    M3DMatrix44f mObjectFrame;
    objectFrame.GetCameraMatrix(mObjectFrame);
    modelViewMatrix.MultMatrix(mObjectFrame);

    //畫圖
    switch(nStep) {
        case 0: DrawPointBatch(&pointBatch); break;
        case 1: DrawLineBatch(&lineBatch); break;
        case 2: DrawLineBatch(&lineStripBatch); break;
        case 3: DrawLineBatch(&lineLoopBatch); break;
        case 4: DrawTriangleBatch(&triangleBatch); break;
        case 5: DrawTriangleBatch(&triangleStripBatch); break;
        case 6: DrawTriangleBatch(&triangleFanBatch); break;
    }
    
    // 還原以前的模型檢視矩陣 (單位矩陣)
    modelViewMatrix.PopMatrix();
    
    //將在後臺緩衝區進行渲染,然後在結束時交換到前臺
    glutSwapBuffers();
}

//特殊按鍵(功能鍵或者方向鍵)監聽
void SpecialKeys(int key, int x, int y) {
    
    //上、下、左、右按鍵,3D 旋轉
    /*
     * RotateWorld(float fAngle, float x, float y, float z)
     * fAngle: 旋轉弧度, x/y/z:以哪個座標軸旋轉
     * m3dDegToRad:角度 -> 弧度
     */
    switch (key) {
        case GLUT_KEY_UP: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f); break;
        case GLUT_KEY_DOWN: objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f); break;
        case GLUT_KEY_LEFT: objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f); break;
        case GLUT_KEY_RIGHT: objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f); break;
        default:
            break;
    }
    
    //觸發渲染
    glutPostRedisplay();
}

//普通按鍵監聽
void KeyPressFunc(unsigned char key, int x, int y) {
    //空格鍵的 key = 32,計算空格鍵的按下次數,6次一迴圈
    if (key == 32) {
        nStep++;
        if (nStep > 6) {
            nStep = 0;
        }
    }
    
    //切換視窗標題
    switch(nStep) {
        case 0: glutSetWindowTitle("GL_POINTS"); break;
        case 1: glutSetWindowTitle("GL_LINES"); break;
        case 2: glutSetWindowTitle("GL_LINE_STRIP"); break;
        case 3: glutSetWindowTitle("GL_LINE_LOOP"); break;
        case 4: glutSetWindowTitle("GL_TRIANGLES"); break;
        case 5: glutSetWindowTitle("GL_TRIANGLE_STRIP"); break;
        case 6: glutSetWindowTitle("GL_TRIANGLE_FAN"); break;
    }
    
    //觸發渲染
    glutPostRedisplay();
}

//視窗大小改變時接受新的寬度和高度
void ChangeSize(int width, int height) {
    
    // 防止下面除法的除數為0導致的閃退
    if(height == 0) height = 1;
    
    //設定檢視視窗位置
    glViewport(0, 0, width, height);
    
    // 建立投影矩陣,並將它載入到投影矩陣堆疊中
    viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    // 在模型檢視矩陣頂部載入單位矩陣
    modelViewMatrix.LoadIdentity();
}

//程式入口
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 | GLUT_STENCIL);
    
    //初始化視窗大小
    glutInitWindowSize(800, 600);
    //建立視窗
    glutCreateWindow("GL_POINTS");
    
    //註冊回撥函式
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    glutKeyboardFunc(KeyPressFunc);
    
    //確保驅動程式的初始化中沒有出現任何問題。
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
        return 1;
    }
    
    //初始化設定
    SetupRC();
    
    //進入呼叫迴圈
    glutMainLoop();
    return 0;
}
複製程式碼

點

線段

線條

閉合線條

三角形

三角形條帶

三角形扇

Demo 原始碼在這裡:github->openGLDemo->03-Primitives

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

相關文章