-
[https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_12](https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_12)
/* 頂點(diǎn)數(shù)組對(duì)象:Vertex Array Object粥惧,VAO 頂點(diǎn)緩沖對(duì)象:Vertex Buffer Object键畴,VBO 索引緩沖對(duì)象:Element Buffer Object,EBO或Index Buffer Object突雪,IBO */ #include "pch.h" //GLEW #include <iostream> #define GLEW_STATIC #include <GL/glew.h> #include <GLFW/glfw3.h> #include "SOIL2/SOIL2.h" #include "glm/glm.hpp" #include "glm/gtc/type_ptr.hpp" #include "Shader.h" void processInput(GLFWwindow *window); //頂點(diǎn)著色器源碼 //頂點(diǎn)著色器主要的目的是把3D坐標(biāo)轉(zhuǎn)為另一種3D坐標(biāo)(后面會(huì)解釋?zhuān)?//同時(shí)頂點(diǎn)著色器允許我們對(duì)頂點(diǎn)屬性進(jì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"; //片段著色器的主要目的是計(jì)算一個(gè)像素的最終顏色,這也是所有OpenGL高級(jí)效果產(chǎn)生的地方咏删。 //通常惹想,片段著色器包含3D場(chǎng)景的數(shù)據(jù)(比如光照、陰影督函、光的顏色等等)嘀粱,這些數(shù)據(jù)可以被用來(lái)計(jì)算最終像素的顏色。 //對(duì)象著色器源碼 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(); //使用flfwWindowHint函數(shù)來(lái)配置GLFW /* glfwWindowHint函數(shù)的第一個(gè)參數(shù)代表選項(xiàng)的名稱侨核, 我們可以從很多以GLFW_開(kāi)頭的枚舉值中選擇草穆; 第二個(gè)參數(shù)接受一個(gè)整型, 用來(lái)設(shè)置這個(gè)選項(xiàng)的值 */ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //glfwCreateWindow函數(shù)需要窗口的寬和高作為它的前兩個(gè)參數(shù) //第三個(gè)參數(shù)表示這個(gè)窗口的名稱 GLFWwindow* window = glfwCreateWindow(800, 600, "B7040312", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } //創(chuàng)建完窗口我們就可以通知GLFW將我們窗口的上下文設(shè)置為當(dāng)前線程的主上下文了 glfwMakeContextCurrent(window); //GLEW是用來(lái)管理OpenGL的函數(shù)指針的搓译,所以在調(diào)用任何OpenGL的函數(shù)之前我們需要初始化GLEW //glewExperimental所做的是即使驅(qū)動(dòng)程序的擴(kuò)展程序字符串中不存在擴(kuò)展程序悲柱,也允許加載擴(kuò)展程序入口點(diǎn)。 glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } /* 開(kāi)始渲染之前還有一件重要的事情要做些己,我們必須告訴OpenGL渲染窗口的尺寸大小豌鸡,這樣OpenGL才只能知道怎樣相對(duì)于窗口大小顯示數(shù)據(jù)和坐標(biāo)嘿般。 我們可以通過(guò)調(diào)用glViewport函數(shù)來(lái)設(shè)置窗口的維度(Dimension): */ /* 然而,當(dāng)用戶改變窗口的大小的時(shí)候涯冠,視口也應(yīng)該被調(diào)整炉奴。 我們可以對(duì)窗口注冊(cè)一個(gè)回調(diào)函數(shù)(Callback Function), 它會(huì)在每次窗口大小被調(diào)整的時(shí)候被調(diào)用 */ int width, height; glfwGetFramebufferSize(window, &width, &height); /* glViewport函數(shù)前兩個(gè)參數(shù)控制窗口左下角的位置蛇更。 第三個(gè)和第四個(gè)參數(shù)控制渲染窗口的寬度和高度(像素)瞻赶,這里我們是直接從GLFW中獲取的。 */ glViewport(0, 0, width, height); //創(chuàng)建著色器對(duì)象 創(chuàng)建類(lèi)型 附加著色器源碼 編譯 // build and compile our shader program // ------------------------------------ // vertex shader(頂點(diǎn)著色器) int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); // check for shader compile errors 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; } // fragment shader(片段著色器) int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); // check for shader compile errors glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } // link shaders int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); // check for linking errors 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); /* 由于我們希望渲染一個(gè)三角形派任,我們一共要指定三個(gè)頂點(diǎn)砸逊,每個(gè)頂點(diǎn)都有一個(gè)3D位置。 我們會(huì)將它們以標(biāo)準(zhǔn)化設(shè)備坐標(biāo)的形式(OpenGL的可見(jiàn)區(qū)域)定義為一個(gè)float數(shù)組掌逛。 由于OpenGL是在3D空間中工作的师逸,而我們渲染的是一個(gè)2D三角形,我們將它頂點(diǎn)的z坐標(biāo)設(shè)置為0.0豆混。 這樣子的話三角形每一點(diǎn)的深度(Depth篓像,譯注2)都是一樣的,從而使它看上去像是2D的皿伺。 */ float vertices[] = { -0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; /* 定義這樣的頂點(diǎn)數(shù)據(jù)以后员辩,我們會(huì)把它作為輸入發(fā)送給圖形渲染管線的第一個(gè)處理階段:頂點(diǎn)著色器。 它會(huì)在GPU上創(chuàng)建內(nèi)存用于儲(chǔ)存我們的頂點(diǎn)數(shù)據(jù)心傀,還要配置OpenGL如何解釋這些內(nèi)存屈暗,并且指定其如何發(fā)送給顯卡拆讯。 頂點(diǎn)著色器接著會(huì)處理我們?cè)趦?nèi)存中指定數(shù)量的頂點(diǎn)脂男。 我們通過(guò)頂點(diǎn)緩沖對(duì)象(Vertex Buffer Objects, VBO)管理這個(gè)內(nèi)存, 它會(huì)在GPU內(nèi)存(通常被稱為顯存)中儲(chǔ)存大量頂點(diǎn)种呐。 使用這些緩沖對(duì)象的好處是我們可以一次性的發(fā)送一大批數(shù)據(jù)到顯卡上宰翅,而不是每個(gè)頂點(diǎn)發(fā)送一次。 從CPU把數(shù)據(jù)發(fā)送到顯卡相對(duì)較慢爽室,所以只要可能我們都要嘗試盡量一次性發(fā)送盡可能多的數(shù)據(jù)汁讼。 當(dāng)數(shù)據(jù)發(fā)送至顯卡的內(nèi)存中后,頂點(diǎn)著色器幾乎能立即訪問(wèn)頂點(diǎn)阔墩,這是個(gè)非澈偌埽快的過(guò)程。 */ //頂點(diǎn)緩沖對(duì)象 //頂點(diǎn)數(shù)組對(duì)象 unsigned int VBO, VAO; //以使用glGenVertexArrays函數(shù)和一個(gè)緩沖ID生成一個(gè)VAO對(duì)象: glGenVertexArrays(1, &VAO); //VBO: //以使用glGenBuffers函數(shù)和一個(gè)緩沖ID生成一個(gè)VBO對(duì)象: //OpenGL有很多緩沖對(duì)象類(lèi)型啸箫,頂點(diǎn)緩沖對(duì)象的緩沖類(lèi)型是GL_ARRAY_BUFFER耸彪。 //OpenGL允許我們同時(shí)綁定多個(gè)緩沖,只要它們是不同的緩沖類(lèi)型忘苛。 //我們可以使用glBindBuffer函數(shù)把新創(chuàng)建的緩沖綁定到GL_ARRAY_BUFFER目標(biāo)上: //從這一刻起蝉娜,我們使用的任何(在GL_ARRAY_BUFFER目標(biāo)上的)緩沖調(diào)用 //都會(huì)用來(lái)配置當(dāng)前綁定的緩沖(VBO)唱较。 glGenBuffers(1, &VBO); //VAO:首先綁定頂點(diǎn)數(shù)組對(duì)象 glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); //VAO:然后把頂點(diǎn)數(shù)組復(fù)制到緩沖中供OpenGL使用 //VBO:然后我們可以調(diào)用glBufferData函數(shù),它會(huì)把之前定義的頂點(diǎn)數(shù)據(jù)復(fù)制到緩沖的內(nèi)存中: glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //VAO:最后設(shè)置頂點(diǎn)屬性指針 //對(duì)glVertexAttribPointer的調(diào)用將VBO注冊(cè)為頂點(diǎn)屬性的綁定頂點(diǎn)緩沖區(qū)對(duì)象召川,因此之后我們可以安全地解除綁定 //鏈接頂點(diǎn)屬性 //每個(gè)頂點(diǎn)屬性從一個(gè)VBO管理的內(nèi)存中獲得它的數(shù)據(jù)南缓,而具體是從哪個(gè)VBO(程序中可以有多個(gè)VBO) //獲取則是通過(guò)在調(diào)用glVertexAttribPointer時(shí)綁定到GL_ARRAY_BUFFER的VBO決定的。 //由于在調(diào)用glVertexAttribPointer之前綁定的是先前定義的VBO對(duì)象荧呐,頂點(diǎn)屬性0現(xiàn)在會(huì)鏈接到它的頂點(diǎn)數(shù)據(jù)汉形。 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //我們現(xiàn)在應(yīng)該使用glEnableVertexAttribArray,以頂點(diǎn)屬性位置值作為參數(shù)倍阐,啟用頂點(diǎn)屬性获雕;頂點(diǎn)屬性默認(rèn)是禁用的 glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); //之后您可以解除綁定VAO,這樣其他VAO調(diào)用就不會(huì)意外修改此VAO收捣,但這很少發(fā)生届案。 修改其他 // VAO無(wú)論如何都需要調(diào)用glBindVertexArray,因此通常在不需要時(shí)罢艾,我們通常不會(huì)取消綁定VAO(也沒(méi)有VBO)楣颠。 glBindVertexArray(0); /* 一般當(dāng)你打算繪制多個(gè)物體時(shí),你首先要生成/配置所有的VAO(和必須的VBO及屬性指針)咐蚯, 然后儲(chǔ)存它們供后面使用童漩。 當(dāng)我們打算繪制物體的時(shí)候就拿出相應(yīng)的VAO,綁定它春锋,繪制完物體后矫膨,再解綁VAO。 */ /* 我們希望程序在我們主動(dòng)關(guān)閉它之前不斷繪制圖像并能夠接受用戶輸入期奔。 因此侧馅,我們需要在程序中添加一個(gè)while循環(huán),我們可以把它稱之為渲染循環(huán)(Render Loop)呐萌, 它能在我們讓GLFW退出前一直保持運(yùn)行馁痴。 下面幾行的代碼就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的渲染循環(huán): */ /* glfwWindowShouldClose函數(shù)在我們每次循環(huán)的開(kāi)始前檢查一次GLFW是否被要求退出, 如果是的話該函數(shù)返回true然后渲染循環(huán)便結(jié)束了肺孤,之后為我們就可以關(guān)閉應(yīng)用程序了罗晕。 */ while (!glfwWindowShouldClose(window)) { // input // ----- processInput(window); // render // ------ //要把所有的渲染(Rendering)操作放到渲染循環(huán)中, //因?yàn)槲覀兿胱屵@些渲染指令在每次渲染循環(huán)迭代的時(shí)候都能被執(zhí)行赠堵。 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // draw our first triangle glUseProgram(shaderProgram); glBindVertexArray(VAO); //我們只有一個(gè)VAO小渊,因此不必每次都綁定它,但是我們這樣做是為了使事情更有條理 //glDrawArrays函數(shù)茫叭,它使用當(dāng)前激活的著色器酬屉,之前定義的頂點(diǎn)屬性配置,和VBO的頂點(diǎn)數(shù)據(jù)(通過(guò)VAO間接綁定)來(lái)繪制圖元杂靶。 glDrawArrays(GL_TRIANGLES, 0, 3); /* glfwSwapBuffers函數(shù)會(huì)交換顏色緩沖(它是一個(gè)儲(chǔ)存著GLFW窗口每一個(gè)像素顏色值的大緩沖)梆惯, 它在這一迭代中被用來(lái)繪制酱鸭,并且將會(huì)作為輸出顯示在屏幕上。 */ /* glfwPollEvents函數(shù)檢查有沒(méi)有觸發(fā)什么事件(比如鍵盤(pán)輸入垛吗、鼠標(biāo)移動(dòng)等)凹髓、更新窗口狀態(tài), 并調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)(可以通過(guò)回調(diào)方法手動(dòng)設(shè)置) */ glfwSwapBuffers(window); glfwPollEvents(); } //delete glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); /* 當(dāng)渲染循環(huán)結(jié)束后我們需要正確釋放/刪除之前的分配的所有資源怯屉。 我們可以在main函數(shù)的最后調(diào)用glfwTerminate函數(shù)來(lái)完成蔚舀。 */ glfwTerminate(); return 0; } void processInput(GLFWwindow *window) { //檢測(cè)是否按下esc,按下則退出循環(huán) if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); }
*效果
image.png
- 用EBO畫(huà)長(zhǎng)方形
/*
頂點(diǎn)數(shù)組對(duì)象:Vertex Array Object锨络,VAO
頂點(diǎn)緩沖對(duì)象:Vertex Buffer Object赌躺,VBO
索引緩沖對(duì)象:Element Buffer Object,EBO或Index Buffer Object羡儿,IBO
*/
#include "pch.h"
//GLEW
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "SOIL2/SOIL2.h"
#include "glm/glm.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "Shader.h"
void processInput(GLFWwindow *window);
//頂點(diǎn)著色器源碼
//頂點(diǎn)著色器主要的目的是把3D坐標(biāo)轉(zhuǎn)為另一種3D坐標(biāo)(后面會(huì)解釋?zhuān)?//同時(shí)頂點(diǎn)著色器允許我們對(duì)頂點(diǎn)屬性進(jì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";
//片段著色器的主要目的是計(jì)算一個(gè)像素的最終顏色,這也是所有OpenGL高級(jí)效果產(chǎn)生的地方掠归。
//通常缅叠,片段著色器包含3D場(chǎng)景的數(shù)據(jù)(比如光照、陰影虏冻、光的顏色等等)肤粱,這些數(shù)據(jù)可以被用來(lái)計(jì)算最終像素的顏色。
//對(duì)象著色器源碼
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();
//使用flfwWindowHint函數(shù)來(lái)配置GLFW
/*
glfwWindowHint函數(shù)的第一個(gè)參數(shù)代表選項(xiàng)的名稱厨相,
我們可以從很多以GLFW_開(kāi)頭的枚舉值中選擇领曼;
第二個(gè)參數(shù)接受一個(gè)整型,
用來(lái)設(shè)置這個(gè)選項(xiàng)的值
*/
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwCreateWindow函數(shù)需要窗口的寬和高作為它的前兩個(gè)參數(shù)
//第三個(gè)參數(shù)表示這個(gè)窗口的名稱
GLFWwindow* window = glfwCreateWindow(800, 600, "B7040312", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
//創(chuàng)建完窗口我們就可以通知GLFW將我們窗口的上下文設(shè)置為當(dāng)前線程的主上下文了
glfwMakeContextCurrent(window);
//GLEW是用來(lái)管理OpenGL的函數(shù)指針的蛮穿,所以在調(diào)用任何OpenGL的函數(shù)之前我們需要初始化GLEW
//glewExperimental所做的是即使驅(qū)動(dòng)程序的擴(kuò)展程序字符串中不存在擴(kuò)展程序庶骄,也允許加載擴(kuò)展程序入口點(diǎn)。
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
/*
開(kāi)始渲染之前還有一件重要的事情要做绪撵,我們必須告訴OpenGL渲染窗口的尺寸大小瓢姻,這樣OpenGL才只能知道怎樣相對(duì)于窗口大小顯示數(shù)據(jù)和坐標(biāo)祝蝠。
我們可以通過(guò)調(diào)用glViewport函數(shù)來(lái)設(shè)置窗口的維度(Dimension):
*/
/*
然而音诈,當(dāng)用戶改變窗口的大小的時(shí)候,視口也應(yīng)該被調(diào)整绎狭。
我們可以對(duì)窗口注冊(cè)一個(gè)回調(diào)函數(shù)(Callback Function)细溅,
它會(huì)在每次窗口大小被調(diào)整的時(shí)候被調(diào)用
*/
int width, height;
glfwGetFramebufferSize(window, &width, &height);
/*
glViewport函數(shù)前兩個(gè)參數(shù)控制窗口左下角的位置。
第三個(gè)和第四個(gè)參數(shù)控制渲染窗口的寬度和高度(像素)儡嘶,這里我們是直接從GLFW中獲取的喇聊。
*/
glViewport(0, 0, width, height);
//創(chuàng)建著色器對(duì)象 創(chuàng)建類(lèi)型 附加著色器源碼 編譯
// build and compile our shader program
// ------------------------------------
// vertex shader(頂點(diǎn)著色器)
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
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;
}
// fragment shader(片段著色器)
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// link shaders
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
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);
/*
由于我們希望渲染一個(gè)三角形,我們一共要指定三個(gè)頂點(diǎn)蹦狂,每個(gè)頂點(diǎn)都有一個(gè)3D位置誓篱。
我們會(huì)將它們以標(biāo)準(zhǔn)化設(shè)備坐標(biāo)的形式(OpenGL的可見(jiàn)區(qū)域)定義為一個(gè)float數(shù)組朋贬。
由于OpenGL是在3D空間中工作的,而我們渲染的是一個(gè)2D三角形窜骄,我們將它頂點(diǎn)的z坐標(biāo)設(shè)置為0.0锦募。
這樣子的話三角形每一點(diǎn)的深度(Depth,譯注2)都是一樣的邻遏,從而使它看上去像是2D的糠亩。
*/
float vertices[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
/*
定義這樣的頂點(diǎn)數(shù)據(jù)以后,我們會(huì)把它作為輸入發(fā)送給圖形渲染管線的第一個(gè)處理階段:頂點(diǎn)著色器准验。
它會(huì)在GPU上創(chuàng)建內(nèi)存用于儲(chǔ)存我們的頂點(diǎn)數(shù)據(jù)赎线,還要配置OpenGL如何解釋這些內(nèi)存,并且指定其如何發(fā)送給顯卡糊饱。
頂點(diǎn)著色器接著會(huì)處理我們?cè)趦?nèi)存中指定數(shù)量的頂點(diǎn)垂寥。
我們通過(guò)頂點(diǎn)緩沖對(duì)象(Vertex Buffer Objects, VBO)管理這個(gè)內(nèi)存,
它會(huì)在GPU內(nèi)存(通常被稱為顯存)中儲(chǔ)存大量頂點(diǎn)另锋。
使用這些緩沖對(duì)象的好處是我們可以一次性的發(fā)送一大批數(shù)據(jù)到顯卡上矫废,而不是每個(gè)頂點(diǎn)發(fā)送一次。
從CPU把數(shù)據(jù)發(fā)送到顯卡相對(duì)較慢砰蠢,所以只要可能我們都要嘗試盡量一次性發(fā)送盡可能多的數(shù)據(jù)蓖扑。
當(dāng)數(shù)據(jù)發(fā)送至顯卡的內(nèi)存中后,頂點(diǎn)著色器幾乎能立即訪問(wèn)頂點(diǎn)台舱,這是個(gè)非陈筛埽快的過(guò)程。
*/
//頂點(diǎn)緩沖對(duì)象
//頂點(diǎn)數(shù)組對(duì)象
unsigned int VBO, VAO, EBO;
//使用glGenVertexArrays函數(shù)和一個(gè)緩沖ID生成一個(gè)VAO對(duì)象:
glGenVertexArrays(1, &VAO);
//VBO:
//使用glGenBuffers函數(shù)和一個(gè)緩沖ID生成一個(gè)VBO對(duì)象:
//OpenGL有很多緩沖對(duì)象類(lèi)型竞惋,頂點(diǎn)緩沖對(duì)象的緩沖類(lèi)型是GL_ARRAY_BUFFER柜去。
//OpenGL允許我們同時(shí)綁定多個(gè)緩沖,只要它們是不同的緩沖類(lèi)型拆宛。
glGenBuffers(1, &VBO);
//EBO:
//使用glGenBuffers函數(shù)和一個(gè)緩沖ID生成一個(gè)EBO對(duì)象:
glGenBuffers(1, &EBO);
//VAO:首先綁定頂點(diǎn)數(shù)組對(duì)象
glBindVertexArray(VAO);
//VBO:
//我們可以使用glBindBuffer函數(shù)把新創(chuàng)建的緩沖綁定到GL_ARRAY_BUFFER目標(biāo)上:
//從這一刻起嗓奢,我們使用的任何(在GL_ARRAY_BUFFER目標(biāo)上的)緩沖調(diào)用
//都會(huì)用來(lái)配置當(dāng)前綁定的緩沖(VBO)。
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//VAO:然后把頂點(diǎn)數(shù)組復(fù)制到緩沖中供OpenGL使用
//VBO:然后我們可以調(diào)用glBufferData函數(shù)浑厚,把索引復(fù)制到緩沖里
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//EBO:
//我們可以使用glBindBuffer函數(shù)把新創(chuàng)建的緩沖綁定到GL_ARRAY_BUFFER目標(biāo)上:
//從這一刻起股耽,我們使用的任何(在GL_ARRAY_BUFFER目標(biāo)上的)緩沖調(diào)用
//都會(huì)用來(lái)配置當(dāng)前綁定的緩沖(VBO)。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//VAO:最后設(shè)置頂點(diǎn)屬性指針
//對(duì)glVertexAttribPointer的調(diào)用將VBO注冊(cè)為頂點(diǎn)屬性的綁定頂點(diǎn)緩沖區(qū)對(duì)象钳幅,因此之后我們可以安全地解除綁定
//鏈接頂點(diǎn)屬性
//每個(gè)頂點(diǎn)屬性從一個(gè)VBO管理的內(nèi)存中獲得它的數(shù)據(jù)物蝙,而具體是從哪個(gè)VBO(程序中可以有多個(gè)VBO)
//獲取則是通過(guò)在調(diào)用glVertexAttribPointer時(shí)綁定到GL_ARRAY_BUFFER的VBO決定的。
//由于在調(diào)用glVertexAttribPointer之前綁定的是先前定義的VBO對(duì)象敢艰,頂點(diǎn)屬性0現(xiàn)在會(huì)鏈接到它的頂點(diǎn)數(shù)據(jù)诬乞。
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
//我們現(xiàn)在應(yīng)該使用glEnableVertexAttribArray,以頂點(diǎn)屬性位置值作為參數(shù),啟用頂點(diǎn)屬性震嫉;頂點(diǎn)屬性默認(rèn)是禁用的
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//之后您可以解除綁定VAO森瘪,這樣其他VAO調(diào)用就不會(huì)意外修改此VAO,但這很少發(fā)生票堵。 修改其他
// VAO無(wú)論如何都需要調(diào)用glBindVertexArray柜砾,因此通常在不需要時(shí),我們通常不會(huì)取消綁定VAO(也沒(méi)有VBO)换衬。
glBindVertexArray(0);
/*
一般當(dāng)你打算繪制多個(gè)物體時(shí)痰驱,你首先要生成/配置所有的VAO(和必須的VBO及屬性指針),
然后儲(chǔ)存它們供后面使用瞳浦。
當(dāng)我們打算繪制物體的時(shí)候就拿出相應(yīng)的VAO担映,綁定它,繪制完物體后叫潦,再解綁VAO蝇完。
*/
/*
我們希望程序在我們主動(dòng)關(guān)閉它之前不斷繪制圖像并能夠接受用戶輸入。
因此矗蕊,我們需要在程序中添加一個(gè)while循環(huán)短蜕,我們可以把它稱之為渲染循環(huán)(Render Loop),
它能在我們讓GLFW退出前一直保持運(yùn)行傻咖。
下面幾行的代碼就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的渲染循環(huán):
*/
/*
glfwWindowShouldClose函數(shù)在我們每次循環(huán)的開(kāi)始前檢查一次GLFW是否被要求退出朋魔,
如果是的話該函數(shù)返回true然后渲染循環(huán)便結(jié)束了,之后為我們就可以關(guān)閉應(yīng)用程序了卿操。
*/
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
//要把所有的渲染(Rendering)操作放到渲染循環(huán)中警检,
//因?yàn)槲覀兿胱屵@些渲染指令在每次渲染循環(huán)迭代的時(shí)候都能被執(zhí)行。
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw our first triangle
glUseProgram(shaderProgram);
glBindVertexArray(VAO); //我們只有一個(gè)VAO害淤,因此不必每次都綁定它扇雕,但是我們這樣做是為了使事情更有條理
//glDrawArrays函數(shù),它使用當(dāng)前激活的著色器窥摄,之前定義的頂點(diǎn)屬性配置镶奉,和VBO的頂點(diǎn)數(shù)據(jù)(通過(guò)VAO間接綁定)來(lái)繪制圖元。
//glDrawArrays(GL_TRIANGLES, 0, 3);
//用glDrawElements來(lái)替換glDrawArrays函數(shù)崭放,來(lái)指明我們從索引緩沖渲染哨苛。
//使用glDrawElements時(shí),我們會(huì)使用當(dāng)前綁定的索引緩沖對(duì)象中的索引進(jìn)行繪制:
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
/*
glfwSwapBuffers函數(shù)會(huì)交換顏色緩沖(它是一個(gè)儲(chǔ)存著GLFW窗口每一個(gè)像素顏色值的大緩沖)莹菱,
它在這一迭代中被用來(lái)繪制移国,并且將會(huì)作為輸出顯示在屏幕上。
*/
/*
glfwPollEvents函數(shù)檢查有沒(méi)有觸發(fā)什么事件(比如鍵盤(pán)輸入道伟、鼠標(biāo)移動(dòng)等)、更新窗口狀態(tài),
并調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)(可以通過(guò)回調(diào)方法手動(dòng)設(shè)置)
*/
glfwSwapBuffers(window);
glfwPollEvents();
}
//delete
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
/*
當(dāng)渲染循環(huán)結(jié)束后我們需要正確釋放/刪除之前的分配的所有資源蜜徽。
我們可以在main函數(shù)的最后調(diào)用glfwTerminate函數(shù)來(lái)完成祝懂。
*/
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
//檢測(cè)是否按下esc,按下則退出循環(huán)
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
-
效果
image.png