Self-learningOpenGL系列——你好析藕,三角形

  • 頂點(diǎn)緩沖對(duì)象(VBO): Vertex Buffer Object
  • 頂點(diǎn)數(shù)組對(duì)象(VAO): Vertex Array Object
  • 索引緩沖對(duì)象(EBO、IBO): Element Buffer Object凳厢、Index Buffer Object

圖形渲染管線

可以被劃分為兩個(gè)主要部分:

  1. 3D坐標(biāo)轉(zhuǎn)換為2D坐標(biāo)
  2. 2D坐標(biāo)轉(zhuǎn)變?yōu)閷?shí)際有顏色的像素

可以被劃分為幾個(gè)階段:

  1. 頂點(diǎn)著色器
  2. 形狀(圖元)裝配
  3. 幾何著色器
  4. 光柵化
  5. 片段著色器
  6. 測(cè)試與混合
頂點(diǎn)著色器

它把一個(gè)單獨(dú)的頂點(diǎn)作為輸入
主要目的是把3D坐標(biāo)轉(zhuǎn)換為另一種3D坐標(biāo)账胧,同時(shí)對(duì)頂點(diǎn)屬性進(jìn)行一些基本處理

圖元裝配

將頂點(diǎn)著色器輸出的所有頂點(diǎn)作為輸入
所有的點(diǎn)裝配成指定圖元形狀

幾何著色器

把圖元形式的一系列頂點(diǎn)的集合作為輸入
可以通過(guò)產(chǎn)生新頂點(diǎn)構(gòu)造出新的圖元來(lái)生成其他形狀

光柵化、裁切

幾何著色器的輸出作為輸入
把圖元映射為最終屏幕上相應(yīng)的像素先紫,生成供片段著色器使用的片段
在片段著色器運(yùn)行之前會(huì)執(zhí)行裁切治泥,丟棄超出視圖以外的所有像素,提升執(zhí)行效率

片段著色器

光柵化生成的片段作為輸入
主要目的是計(jì)算一個(gè)像素的最終顏色

測(cè)試與混合

片段著色器的輸出作為輸入
檢測(cè)片段的對(duì)應(yīng)的深度值泡孩,判斷這個(gè)像素是其它物體的前面還是后面车摄,決定是否應(yīng)該丟棄;檢查Alpha值并對(duì)物體進(jìn)行混合

數(shù)據(jù)輸入

定義一個(gè)float數(shù)組:

//以標(biāo)準(zhǔn)化設(shè)備坐標(biāo)形式
float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
};

通過(guò)VBO管理這個(gè)內(nèi)存仑鸥,代碼會(huì)像這樣:

//0:生成頂點(diǎn)緩沖對(duì)象
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//1:復(fù)制數(shù)據(jù)到緩沖內(nèi)存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

著色器

1)編寫(xiě)程序

//頂點(diǎn)著色器程序
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"
    "}\0";
//片段著色器程序
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"
    "}\n\0";

2)編譯源碼吮播,代碼會(huì)像這樣:

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

GL_VERTEX_SHADER表示頂點(diǎn)著色器類型
GL_FRAGMENT_SHADER表示片段著色器類型

檢測(cè)編譯是否成功:

int  success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

if(!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}

3)鏈接著色器程序,代碼會(huì)像這樣:

unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

檢測(cè)鏈接是否成功:

int  success;
char infoLog[512];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);

if(!success)
{
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::LINK_FAILED\n" << infoLog << std::endl;
}

4)激活眼俊、使用程序
glUseProgram(shaderProgram);

把著色器對(duì)象鏈接到程序?qū)ο笠院笠夂荩浀脛h除著色器對(duì)象:

glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

我們已經(jīng)把輸入頂點(diǎn)數(shù)據(jù)發(fā)送給了GPU,并指示了GPU如何在頂點(diǎn)和片段著色器中處理它疮胖。但是环戈,OpenGL還不知道它該如何解釋內(nèi)存中的頂點(diǎn)數(shù)據(jù),以及它該如何將頂點(diǎn)數(shù)據(jù)鏈接到頂點(diǎn)著色器的屬性上澎灸。

鏈接頂點(diǎn)屬性

使用glVertexAttribPointer函數(shù)解析頂點(diǎn)數(shù)據(jù)院塞;
使用glEnableVertexAttribArray啟動(dòng)頂點(diǎn)屬性,默認(rèn)是禁用的性昭;

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

頂點(diǎn)數(shù)組對(duì)象

存儲(chǔ)內(nèi)容:

  • glEnableVertexAttribArrayglDisableVertexAttribArray的調(diào)用
  • 通過(guò)glVertexAttribPointer設(shè)置的頂點(diǎn)屬性配置
  • 通過(guò)glVertexAttribPointer調(diào)用與頂點(diǎn)屬性關(guān)聯(lián)的頂點(diǎn)緩沖對(duì)象

代碼會(huì)像這樣:

// ..:: 初始化代碼(只運(yùn)行一次 (除非你的物體頻繁改變)) :: ..
// 1. 綁定VAO
glBindVertexArray(VAO);
// 2. 把頂點(diǎn)數(shù)組復(fù)制到緩沖中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 設(shè)置頂點(diǎn)屬性指針
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

[...]

// ..:: 繪制代碼(渲染循環(huán)中) :: ..
// 4. 繪制物體
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();

繪制三角形

代碼會(huì)像這樣:

glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

完整程序

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

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

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"
"}\0";
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"
"}\n\0";

int main()
{
    // glfw: 初始化和配置
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 主版本號(hào)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 次版本號(hào)
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心模式
    
#ifdef __?__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // macOS中需要加上這行代碼拦止,配置才能生效
#endif
    
    // glfw: 創(chuàng)建窗口對(duì)象
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window); // 將窗口的上下文設(shè)置為當(dāng)前線程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    // glad: 初始化,加載所有OpenGL函數(shù)指針
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    
    // 構(gòu)建和編譯著色器程序
    // ------------------------------------
    // 頂點(diǎn)著色器
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 片段著色器
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 鏈接著色器
    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    
    // 設(shè)置頂點(diǎn)數(shù)據(jù)(和緩沖區(qū))并配置頂點(diǎn)屬性
    // ------------------------------------------------------------------
    float vertices[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f,
    };
    
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // 首先綁定頂點(diǎn)數(shù)組對(duì)象糜颠,然后綁定和設(shè)置頂點(diǎn)緩沖區(qū)汹族,然后配置頂點(diǎn)屬性。
    glBindVertexArray(VAO);
    
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    //解綁
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    
    // 渲染循環(huán)
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // 輸入
        // -----
        processInput(window);
        
        // 渲染
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        // 畫(huà)出第一個(gè)三角形
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        
        // 交換緩沖區(qū)和輪詢IO事件(按下/釋放鍵其兴,移動(dòng)鼠標(biāo)等)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    
    // 正確釋放/刪除之前的分配的所有資源
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// 處理所有輸入:查詢GLFW是否按下/釋放了此幀的相關(guān)鍵顶瞒,并做出相應(yīng)的反應(yīng)
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// 在每次窗口大小被調(diào)整的時(shí)候被調(diào)用
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}
屏幕快照

索引緩沖對(duì)象

和頂點(diǎn)緩沖對(duì)象一樣,EBO也是一個(gè)緩沖元旬,它專門(mén)儲(chǔ)存索引
代碼會(huì)像這樣:

// ..:: 初始化代碼 :: ..
// 1. 綁定頂點(diǎn)數(shù)組對(duì)象
glBindVertexArray(VAO);
// 2. 把我們的頂點(diǎn)數(shù)組復(fù)制到一個(gè)頂點(diǎn)緩沖中榴徐,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 復(fù)制我們的索引數(shù)組到一個(gè)索引緩沖中守问,供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 設(shè)定頂點(diǎn)屬性指針
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

[...]

// ..:: 繪制代碼(渲染循環(huán)中) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0);
屏幕快照

GitHub

Self-learningOpenGL

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箕速,隨后出現(xiàn)的幾起案子酪碘,更是在濱河造成了極大的恐慌朋譬,老刑警劉巖盐茎,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異徙赢,居然都是意外死亡字柠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)狡赐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窑业,“玉大人,你說(shuō)我怎么就攤上這事枕屉〕1” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵搀擂,是天一觀的道長(zhǎng)西潘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)哨颂,這世上最難降的妖魔是什么喷市? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮威恼,結(jié)果婚禮上品姓,老公的妹妹穿的比我還像新娘。我一直安慰自己箫措,他們只是感情好腹备,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著斤蔓,像睡著了一般植酥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上附迷,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天惧互,我揣著相機(jī)與錄音,去河邊找鬼喇伯。 笑死喊儡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稻据。 我是一名探鬼主播艾猜,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼买喧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了匆赃?” 一聲冷哼從身側(cè)響起淤毛,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎算柳,沒(méi)想到半個(gè)月后低淡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞬项,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年蔗蹋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囱淋。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猪杭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妥衣,到底是詐尸還是另有隱情皂吮,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布税手,位于F島的核電站蜂筹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏冈止。R本人自食惡果不足惜狂票,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望熙暴。 院中可真熱鬧闺属,春花似錦、人聲如沸周霉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)俱箱。三九已至国瓮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狞谱,已是汗流浹背乃摹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跟衅,地道東北人孵睬。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伶跷,于是被迫代替她去往敵國(guó)和親掰读。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秘狞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 你好,三角形 圖形渲染管線(Pipeline) 3D坐標(biāo)轉(zhuǎn)為2D坐標(biāo)的處理過(guò)程是由OpenGL的圖形渲染管線(Pi...
    IceMJ閱讀 7,440評(píng)論 2 13
  • 本文首發(fā)于個(gè)人博客:Lam's Blog - 【OpenGL ES】入門(mén)及繪制一個(gè)三角形蹈集,文章由MarkDown語(yǔ)...
    格子林ll閱讀 7,269評(píng)論 2 18
  • OpenGL基礎(chǔ)詞匯理解 1.單詞術(shù)語(yǔ):(1) 頂點(diǎn)數(shù)組對(duì)象:Vertex Array Object拢肆,VAO(2)...
    測(cè)試開(kāi)發(fā)雨辰閱讀 629評(píng)論 0 1
  • ——班主任工作反思 晉城愛(ài)物學(xué)校 時(shí)慧慧 一善榛、我有一個(gè)夢(mèng)想 當(dāng)老師辩蛋,是我童年的夢(mèng)想呻畸。我的教師情結(jié)始于蒙童移盆,是小學(xué)的...
    時(shí)慧慧愛(ài)物閱讀 410評(píng)論 0 4
  • 該是御街向曲江,棗紅駒伤为,黃馬褂咒循。 瑤臺(tái)仙曲檀香漫,瓊露迷醉琵琶绞愚。 風(fēng)華正茂叙甸,雙八年華,談笑斷亂麻位衩。 霉雨看盡空垂淚...
    白鳥(niǎo)五月閱讀 381評(píng)論 0 1