OpenGL學習筆記(九):索引緩衝器(EBO /IBE)的理解與使用,引入線框/填充模式


原博主博客地址:http://blog.csdn.net/qq21497936
本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78942027ios


《OpenGL學習筆記》系列博客目錄地址:http://blog.csdn.net/qq21497936/article/category/7315532
數組


OpenGL學習筆記():索引緩衝器(EBO /IBE)的理解與使用,引入線框/填充模式



前話


        前面一章節介紹了VAO、VBO和SHADER的基本使用,本章節將繼續引入索引緩衝器,使用索引緩衝器創建一個六邊形,並使六邊形兩兩相鄰的三個頂點構成一個三角形,共六個角形(每一個頂點被使用3次),並引入繪畫填充模式。

Demo效果圖





專業名詞


        EBO(Element Buffer Object)、EBO(Index Buffer Object):索引緩衝對象。緩存


索引緩衝對象


        索引緩衝對象專門儲存索引,OpenGL調用這些頂點的索引來決定該繪製哪一個頂點,使用索引可重複使用同一個頂點。函數

不使用索引緩衝器繪製

        首先咱們先看六邊形的六個頂點座標工具

       

        在不使用索引的狀況下,咱們的頂點數據以下:oop

// 7.頂點數據
    float vertices[] = {
        -0.866, -0.5, 0.0,
        -0.866,  0.5, 0.0,
         0.0  ,  1.0, 0.0, // 三角形1

        -0.866,  0.5, 0.0,
         0.0  ,  1.0, 0.0,
         0.866,  0.5, 0.0, // 三角形2

         0.0  ,  1.0, 0.0,
         0.866,  0.5, 0.0,
         0.866, -0.5, 0.0, // 三角形3

         0.866,  0.5, 0.0,
         0.866, -0.5, 0.0,
         0.0  , -1.0, 0.0, // 三角形4

         0.866, -0.5, 0.0,
         0.0  , -1.0, 0.0,
        -0.866, -0.5, 0.0, // 三角形5

         0.0  , -1.0, 0.0,
        -0.866, -0.5, 0.0,
        -0.866,  0.5, 0.0, // 三角形6
};

        繪製的頂點數量,是18個,因此修改程序的繪製頂點數量:學習

while (!glfwWindowShouldClose(window))
    {
        ……
        // 繪製三角形:三角形        數組起始索引 繪製多少個頂點
        //        glDrawArrays(GL_TRIANGLES, 0,         3);
        glDrawArrays(GL_TRIANGLES, 0,         18); 
        ……
}

使用索引緩衝器繪製

        使用索引緩衝器,其實咱們全部的三角形只使用到了六個頂點(須要重複使用的經過索引可重複使用),VAO中就保存該6個頂點能夠了:ui

// 7.0索引緩衝期 增長的索引緩衝器頂點索引
    unsigned int indices[] = { // 注意索引從0開始!
        0, 1, 2, // 第一個三角形
        1, 2, 3, // 第二個三角形
        2, 3, 4, // 第三個三角形
        3, 4, 5, // 第四個三角形
        4, 5, 0, // 第五個三角形
        5, 0, 1, // 第六個三角形
};

        使用EBO和VBO同樣,首先是使用過程是同樣的,其次是使用的注意點也是同樣的,就是綁定EBO/VBO到解綁EBO/VBO(須要生效的)的過程必定要在綁定頂點數據和解綁頂點數據之間,否該繪製該頂點,緩存未被生效。url

        如下是在原來的VAO,VBO代碼部分新增了EBO的代碼:spa

// 8.使用VAO和VBO
    unsigned int VBO, VAO;
    // glGenVertexArrays() 建立一個頂點數組對象
    // 第一個參數:須要建立的緩存數量
    // 第二個參數:存儲單一ID或多個ID的GLuint變量或數組的地址。
    glGenVertexArrays(1, &VAO);

    // glGenBuffers() 建立一個緩存對象而且返回緩存對象的標示符。
    glGenBuffers(1, &VBO);

    // 頂點對象建立以後,在使用緩存對象以前,須要將緩存對象鏈接到相應的緩存上。
    // glBindBuffer()有1個參數:buffer。(綁定和解綁的順序很重要,勿更改)
    glBindVertexArray(VAO);

    // 緩存對象建立以後,在使用緩存對象以前,須要將緩存對象鏈接到相應的緩存上。
    // glBindBuffer()有2個參數:target與buffer。(綁定和解綁的順序很重要,勿更改)
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 當緩存初始化以後,使用glBufferData()將頂點數據拷貝到緩存對象
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

#if 1  // 索引緩衝器-添加代碼
    // 8.0 與VBO相似,先建立EBO對象,再先綁定EBO
    //     而後用glBufferData把索引複製到緩衝裏
    //     一樣,和VBO相似,把這些函數調用放在綁定和解綁函數調用之間
    //     區別在於,只不過此次咱們把緩衝的類型定義爲GL_ELEMENT_ARRAY_BUFFER。
    unsigned int EBO;
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
#endif
    // 設置頂點屬性指針,glVertexAttribPointer()函數告訴OpenGL該如何解析頂點數據
    //                    頂點屬性位置 頂點屬性大小 數據的類型 是否被標準化 步長             偏移
    glVertexAttribPointer(0,         3,         GL_FLOAT, GL_FALSE,  3*sizeof(float), (void *)0);
    // glEnableVertexAttribArray()以頂點屬性位置值做爲參數,啓用頂點屬性;頂點屬性默認是禁用的
    glEnableVertexAttribArray(0);

    // 解綁緩存着色器(綁定和解綁的順序很重要,勿更改)
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 解綁頂點着色器(綁定和解綁的順序很重要,勿更改)
    glBindVertexArray(0);

        修改後的繪製循環代碼:

while (!glfwWindowShouldClose(window))
    {
        // 捕捉輸入
        processInput(window);
        // 清空顏色緩衝
        // 添加渲染指令,背景RGBA的A不生效, 更改成白色
        glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        /* 新增代碼 第二段/共三段----------------------------------START */
        // 當咱們渲染一個物體時要使用着色器程序
        glUseProgram(shaderProgram);
        // 激活VAO表示的頂點緩存
        glBindVertexArray(VAO);
        // 每次從新更新視口,若是不這樣,拉昇窗口後,會變形。
        // 你把你要觀察的視景體映射到你的視口就沒有問題,可是若是你不設置視景體,
        // opengl默認會以屏幕座標,就是屏幕中心爲原點,到四周的距離爲1的這個範圍
        // 做爲視景體的觀察部分映射到你設置的視口上。
        // 能夠嘗試,屏蔽此句,而後運行程序拉昇窗口試試效果,更好理解
//        glViewport(0, 0, 600, 600);
        // 繪製三角形:三角形        數組起始索引 繪製多少個頂點
        // 不使用索引緩衝器繪製頂點
#if 1
        // 繪製六邊的兩兩鏈接的三角形
//        glDrawArrays(GL_TRIANGLES, 0,         18);
#endif
#if 1
        // 索引緩衝器-添加代碼      18個索引點(三角形,3個因此組成一個三角形)
        //                        這裏18表明是18個索引,頂點數座標也是18,只是巧合罷了
        glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, 0);
#endif
        // 繪製三角形:線條      數組起始索引 繪製多少個頂點
//        glDrawArrays(GL_LINES, 0          , 2);
        /* 新增代碼 第二段/共三段----------------------------------END */

        // 檢查並調用事件,交換緩衝(執行交換緩存纔會更新新數據到界面)
        glfwSwapBuffers(window);

        glfwPollEvents();
}

運行結果

源代碼

#include <iostream>
#include <process.h>
#include <GLFW/glfw3.h>
#include <glad/glad.h>

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

// 在GLFW中實現一些輸入控制,使用GLFW的glfwGetKey函數
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

int main()
{
    glfwInit();
    /*
       請確認您的系統支持OpenGL3.3或更高版本,不然此應用有可能會
       崩潰或者出現不可預知的錯誤。若是想要查看OpenGL版本的話,
       在Linux上運行glxinfo,或者在Windows上使用其它的工具(例如
       OpenGL Extension Viewer)。若是你的OpenGL版本低於3.3,檢
       查一下顯卡是否支持OpenGL 3.3+(不支持的話你的顯卡真的太老
       了),並更新你的驅動程序,有必要的話請更新顯卡。
    */
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    // 若是使用的是Mac OS X系統,你還須要加下面這行代碼到你的
    // 初始化代碼中這些配置才能起做用
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    // 建立一個窗口對象,這個窗口對象存放了全部和窗口相關的數據,
    // 並且會被GLFW的其餘函數頻繁地用到
    // glfwCreateWindow函數須要窗口的寬和高做爲它的前兩個參數。
    // 第三個參數表示這個窗口的名稱(標題),最後兩個參數暫時忽略。
    // 函數會返回一個GLFWwindow對象,咱們會在其它的GLFW操做中使用到。
    GLFWwindow* window = glfwCreateWindow(800, 600, "QQ:21497936", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // GLAD是用來管理OpenGL的函數指針的,因此在調用任何OpenGL的函數
    // 以前咱們須要初始化GLAD。
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // 還須要註冊這個函數,告訴GLFW咱們但願每當窗口調整大小的時候調用這個函數。
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 咱們必須告訴OpenGL渲染窗口的尺寸大小,即視口(Viewport),
    // 這樣OpenGL才只能知道怎樣根據窗口大小顯示數據和座標。
    // 能夠經過調用glViewport函數來設置窗口的維度(Dimension)。
    // OpenGL幕後使用glViewport中定義的位置和寬高進行2D座標的轉
    // 換,將OpenGL中的位置座標轉換爲你的屏幕座標。例如,OpenGL
    // 中的座標(-0.5, 0.5)有可能(最終)被映射爲屏幕中的座標(200,450)。
    // 注意,處理過的OpenGL座標範圍只爲-1到1,所以咱們事實上
    // 將(-1到1)範圍內的座標映射到(0, 800)和(0, 600)。
    glViewport(0, 0, 600, 600);

    // 在咱們主動關閉它以前不斷繪製圖像並可以接受用戶輸入。所以,
    // 須要在程序中添加一個while循環,咱們能夠把它稱之爲渲染循環(Render Loop),
    // 它能在咱們讓GLFW退出前一直保持運行。

    /* 新增代碼 第一段/共三段----------------------------------START */

    // 1.頂點着色器
    const char *vertexShaderSource = \
        "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "}";
    // 2.片斷着色器
    const char *fragmentShaderSource = \
        "#version 330 core\n"
        "out vec4 FragColor;\n"
        "void main()\n"
        "{\n"
        "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
        "}";
    // 3.建立頂點着色器並編譯
    int success;
    char infoLog[512];
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 4.建立片斷着色器並編譯
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 5.鏈接着色器
    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 6.刪除已經使用的着色器(由於後面再也不須要綁定了)
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
#if 0
    // 7.頂點數據
    float vertices[] = {
        -0.866, -0.5, 0.0,
        -0.866,  0.5, 0.0,
         0.0  ,  1.0, 0.0, // 三角形1

        -0.866,  0.5, 0.0,
         0.0  ,  1.0, 0.0,
         0.866,  0.5, 0.0, // 三角形2

         0.0  ,  1.0, 0.0,
         0.866,  0.5, 0.0,
         0.866, -0.5, 0.0, // 三角形3

         0.866,  0.5, 0.0,
         0.866, -0.5, 0.0,
         0.0  , -1.0, 0.0, // 三角形4

         0.866, -0.5, 0.0,
         0.0  , -1.0, 0.0,
        -0.866, -0.5, 0.0, // 三角形5

         0.0  , -1.0, 0.0,
        -0.866, -0.5, 0.0,
        -0.866,  0.5, 0.0, // 三角形6
    };
#endif
#if 1 // 索引緩衝器-添加代碼
    // 7.頂點數據
    float vertices[] = {
        -0.866, -0.5, 0.0, //      1  三角形5
        -0.866,  0.5, 0.0, //      1 2  三角形6
         0.0  ,  1.0, 0.0, // 三角形1 2 3
         0.866,  0.5, 0.0, //   三角形2 3 4
         0.866, -0.5, 0.0, //     三角形3 4 5
         0.0  , -1.0, 0.0, //       三角形4 5 6
    };
    // 7.0索引緩衝期 增長的索引緩衝器頂點索引
    unsigned int indices[] = { // 注意索引從0開始!
        0, 1, 2, // 第一個三角形
        1, 2, 3, // 第二個三角形
        2, 3, 4, // 第三個三角形
        3, 4, 5, // 第四個三角形
        4, 5, 0, // 第五個三角形
        5, 0, 1 // 第六個三角形
    };
#endif
    // 8.使用VAO和VBO
    unsigned int VBO, VAO;
    // glGenVertexArrays() 建立一個頂點數組對象
    // 第一個參數:須要建立的緩存數量
    // 第二個參數:存儲單一ID或多個ID的GLuint變量或數組的地址。
    glGenVertexArrays(1, &VAO);

    // glGenBuffers() 建立一個緩存對象而且返回緩存對象的標示符。
    glGenBuffers(1, &VBO);

    // 頂點對象建立以後,在使用緩存對象以前,須要將緩存對象鏈接到相應的緩存上。
    // glBindBuffer()有1個參數:buffer。(綁定和解綁的順序很重要,勿更改)
    glBindVertexArray(VAO);

    // 緩存對象建立以後,在使用緩存對象以前,須要將緩存對象鏈接到相應的緩存上。
    // glBindBuffer()有2個參數:target與buffer。(綁定和解綁的順序很重要,勿更改)
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 當緩存初始化以後,使用glBufferData()將頂點數據拷貝到緩存對象
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

#if 1  // 索引緩衝器-添加代碼
    // 8.0 與VBO相似,先建立EBO對象,再先綁定EBO
    //     而後用glBufferData把索引複製到緩衝裏
    //     一樣,和VBO相似,把這些函數調用放在綁定和解綁函數調用之間
    //     區別在於,只不過此次咱們把緩衝的類型定義爲GL_ELEMENT_ARRAY_BUFFER。
    unsigned int EBO;
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
#endif
    // 設置頂點屬性指針,glVertexAttribPointer()函數告訴OpenGL該如何解析頂點數據
    //                    頂點屬性位置 頂點屬性大小 數據的類型 是否被標準化 步長             偏移
    glVertexAttribPointer(0,         3,         GL_FLOAT, GL_FALSE,  3*sizeof(float), (void *)0);
    // glEnableVertexAttribArray()以頂點屬性位置值做爲參數,啓用頂點屬性;頂點屬性默認是禁用的
    glEnableVertexAttribArray(0);

    // 解綁緩存着色器(綁定和解綁的順序很重要,勿更改)
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 解綁頂點着色器(綁定和解綁的順序很重要,勿更改)
    glBindVertexArray(0);
    /* 新增代碼 第一段/共三段----------------------------------END */

    while (!glfwWindowShouldClose(window))
    {
        // 捕捉輸入
        processInput(window);
        // 清空顏色緩衝
        // 添加渲染指令,背景RGBA的A不生效, 更改成白色
        glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        /* 新增代碼 第二段/共三段----------------------------------START */
        // 當咱們渲染一個物體時要使用着色器程序
        glUseProgram(shaderProgram);
        // 激活VAO表示的頂點緩存
        glBindVertexArray(VAO);
        // 每次從新更新視口,若是不這樣,拉昇窗口後,會變形。
        // 你把你要觀察的視景體映射到你的視口就沒有問題,可是若是你不設置視景體,
        // opengl默認會以屏幕座標,就是屏幕中心爲原點,到四周的距離爲1的這個範圍
        // 做爲視景體的觀察部分映射到你設置的視口上。
        // 能夠嘗試,屏蔽此句,而後運行程序拉昇窗口試試效果,更好理解
//        glViewport(0, 0, 600, 600);
        // 繪製三角形:三角形        數組起始索引 繪製多少個頂點
        // 不使用索引緩衝器繪製頂點
#if 1
        // 繪製六邊的兩兩鏈接的三角形
//        glDrawArrays(GL_TRIANGLES, 0,         18);
#endif
#if 1
        // 索引緩衝器-添加代碼      18個索引點(三角形,3個因此組成一個三角形)
        //                        這裏18表明是18個索引,頂點數座標也是18,只是巧合罷了
        glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, 0);
#endif
        // 繪製三角形:線條      數組起始索引 繪製多少個頂點
//        glDrawArrays(GL_LINES, 0          , 2);
        /* 新增代碼 第二段/共三段----------------------------------END */

        // 檢查並調用事件,交換緩衝(執行交換緩存纔會更新新數據到界面)
        glfwSwapBuffers(window);

        glfwPollEvents();
    }

    /* 新增代碼 第三段/共三段----------------------------------END */
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    /* 新增代碼 第三段/共三段----------------------------------END */

    // 當渲染循環結束後咱們須要正確釋放/刪除以前的分配的全部資源。
    glfwTerminate();
    return 0;
}


拓展

        只畫邊框(不填充,相似於畫圖的畫刷),加上以下代碼:

#if 1
    // 使用xiankuan線框模式 隨便放哪裏,只要再glad以後(由於glad以後才能準確找到gl函數地址)
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endi

線框模式(Wireframe Mode)

        想用線框模式繪製圖形,可知填充模式,經過glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)函數配置OpenGL如何繪製圖元。第一個參數表示咱們打算將其應用到全部的三角形的正面和背面,第二個參數告訴用線來繪製。以後的繪製調用會一直以線框模式繪製三角形,直到咱們用glPolygonMode(GL_FRONT_AND_BACK,GL_FILL)將其設置回默認模式。

拓展效果







原博主博客地址:http://blog.csdn.net/qq21497936
本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78942027


本文同步分享在 博客「紅胖子(AAA紅模仿)」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索