寫在前面
從這節開始,會接觸到很多基本概念,原書我也是讀了很多遍,一遍一遍去理解其中的意思,以及他們之間的關係。
概念
頂點數組對象:VAO
頂點緩衝對象:VBO
索引緩衝對象:EBO|IBO
OpenGL是一個3D空間,而螢幕和視窗是2D的,所以OpenGL的大部分工作是將3D座標轉換為2D像素
轉換的過程就是由圖形渲染管線管理的。
映像渲染管線分為兩部分:1.把3D座標轉換為2D座標。2.把2D座標轉變為有顏色的像素
渲染的幾個階段:
頂點資料->頂點著色器->圖元裝配->幾何著色器->光柵化->片段著色器->測試與混合
渲染的每一個階段都有自己的程式,該程式是使用OpenGL著色器語言進行編寫的,這些小程式叫做著色器(shader)
頂點著色器:把3D座標轉換為另一種3d座標,同時對頂點屬性進行一些基本處理
圖元裝配:將所有的點裝配成指定的圖元形狀
幾何著色器:通過產生新的點和新的圖元來產生形狀
光柵化:將圖元映射為最終螢幕上相應的像素,產生片段著色器使用的片段,並裁切吊超過視圖以外的像素
片段著色器:計算一個像素的最終顏色
測試與混合:檢測片段對應的深度值,用來判斷這個像素是其它物體的前面還是後面,決定是否丟棄
代碼建立
首先,我們聲明出來下面需要使用的著色器源碼。
通過上述概念我們知道著色器,是運行在映像渲染管線上的小程式,代碼風格類似c語言
1 // 頂點著色器源碼 2 const char *vertexShaderSource = 3 "#version 330 core\n" 4 "layout (location = 0) in vec3 aPos;\n" 5 "void main(){\n" 6 "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n" 7 "}\n\0"; 8 9 //片段著色器源碼10 const char *fragmentShaderSource =11 "#version 330 core\n"12 "out vec4 FragColor;\n"13 "void main(){\n"14 "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"15 "}\n\0";
下邊我們使用上述兩個著色器,構建一個可以渲染的渲染管線
//建立頂點著色器 int vertexShader = glCreateShader(GL_VERTEX_SHADER); // 第一個參數著色器對象,第二個參數傳遞的源碼字串,第三個參數頂點著色器的源碼 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); // 判斷編譯是否成功 int sucess; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &sucess); if(!sucess){ 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, & sucess); if(!sucess){ 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); glGetShaderiv(shaderProgram, GL_COMPILE_STATUS, & sucess); if(!sucess){ glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog); std::cout<<"ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n"<< infoLog<<std::endl; }
連結兩個著色器到我們剛建立的著色器程式中,且進行了編譯,這個時候,我們上面的兩個著色器,已經在shaderProgram中了。
1 //刪除無用的頂點著色器以及片段著色器 2 glDeleteShader(vertexShader);3 glDeleteShader(fragmentShader);
我們知道,渲染的幾個階段,那麼現在我們只需要建立頂點資料,然後使用上述著色器程式進行渲染
1 // 定義三個頂點2 float vertices[] = {3 -0.5f, -0.5f, 0.0f,4 0.5f, -0.5f, 0.0f,5 0.0f, 0.5f, 0.0f6 };
對於頂點的管理,我們使用頂點緩衝對象以及頂點數組對象進行管理。文章開頭已經講解了基本的概念
// 使用頂點緩衝對象進行緩衝 unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // 緩衝對象進行綁定 glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO);
緩衝對象的使用
1 // 將定義的頂點資料複製到緩衝記憶體中 2 // 第一個參數,緩衝類型,第二個參數資料大小,第三個參數發送的資料,第四個參數,顯卡如何管理給定的資料 3 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 4 5 // 解析頂點資料 6 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); 7 // 啟動頂點資料 8 glEnableVertexAttribArray(0); 9 10 glBindBuffer(GL_ARRAY_BUFFER, 0);11 glBindVertexArray(0);12 13 glDrawArrays(GL_TRIANGLES,0,3);
現在我們的頂點數組對象已經在記憶體中了
下面就是調用著色器程式進行啟動
1 //啟用三角形2 glClearColor(0.2f, 0.3f, 0.3f, 1.0f);3 glClear(GL_COLOR_BUFFER_BIT);4 // 啟用程式5 glUseProgram(shaderProgram);6 glBindVertexArray(VAO);7 glDrawArrays(GL_TRIANGLES, 0, 3);8 glfwSwapBuffers(window);
最後,不要忘了清楚我們使用的vao以及vbo對象
//清除對象 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO);
總結
通過上述的流程,應該是可以跑起來一個三角形的。OpenGL之路漫漫長,我們繼續加油