1 #define GLEW_STATIC 2 #include <GL\glew.h> 3 #include <GLFW\glfw3.h> 4 #include<iostream> 5 using namespace std; 6 7 //函數原型 8 void key_callback(GLFWwindow* window, int key, int scancode, 9 int action, int mode); 10 11 //窗口大小 12 const GLuint WIDTH = 800, HEIGHT = 600; 13 14 const GLchar* vertexShaderSource = "#version 330 core\n" 15 "layout (location = 0) in vec3 position;\n" 16 "void main()\n" 17 "{\n" 18 "gl_Position = vec4(position.x,position.y,position.z,1.0);\n" 19 "}\0"; 20 21 const GLchar* fragmentShaderSource = "#version 330 core\n" 22 "out vec4 color;\n" 23 "void main()\n" 24 "{\n" 25 "color = vec4(1.0f,0.5f,0.2f,1.0f);\n" 26 "}\n\0"; 27 28 int main(){ 29 //初始化 GLFW 30 glfwInit(); 31 32 //設置GLFW需要的選項 33 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 34 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 35 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 36 glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 37 38 //創建一個窗口對象 39 GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "test2", nullptr, nullptr); 40 glfwMakeContextCurrent(window); 41 42 //設置徐亞的回調函數 43 glfwSetKeyCallback(window, key_callback); 44 45 glewExperimental = GL_TRUE; 46 47 glewInit(); 48 49 int width, height; 50 glfwGetFramebufferSize(window, &width, &height); 51 glViewport(0, 0, width, height); 52 53 //定點着色器 54 GLuint vertextShader; 55 vertextShader = glCreateShader(GL_VERTEX_SHADER); 56 glShaderSource(vertextShader, 1, &vertexShaderSource, NULL); 57 glCompileShader(vertextShader); 58 59 //用於判斷一個着色器是否編譯成功 60 GLint success; 61 GLchar infoLog[512]; 62 glGetShaderiv(vertextShader, GL_COMPILE_STATUS, &success); 63 if (!success){ 64 glGetShaderInfoLog(vertextShader, 512, NULL, infoLog); 65 cout << "vertextShader COMPILE FAILED" << endl; 66 } 67 68 //像素着色器 69 GLuint fragmentShader; 70 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 71 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); 72 glCompileShader(fragmentShader); 73 74 //用於判斷一個着色器是否編譯成功 75 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 76 if (!success){ 77 glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); 78 cout << "fragmentShader COMPILE FAILED" << endl; 79 } 80 81 //創建一個着色器程序對象:多個着色器最后鏈接的版本 82 GLuint shaderProgram; 83 shaderProgram = glCreateProgram(); 84 glAttachShader(shaderProgram, vertextShader); 85 glAttachShader(shaderProgram, fragmentShader); 86 glLinkProgram(shaderProgram); 87 88 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); 89 90 if (!success){ 91 glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); 92 } 93 glDeleteShader(vertextShader); 94 glDeleteShader(fragmentShader); 95 96 //創建定點數據 97 GLfloat vertices[] = { 98 -0.5f, -0.5f, 0.0f, 99 0.5f, -0.5f, 0.0f, 100 0.0f, 0.5f, 0.0f 101 }; 102 //創建 VBO 頂點緩沖數據,並把數據賦值到內存中 103 GLuint VBO; 104 glGenBuffers(1, &VBO); 105 //VAO頂點數組對象 106 GLuint VAO; 107 glGenVertexArrays(1, &VAO); 108 109 //綁定VAO 110 glBindVertexArray(VAO); 111 112 //復制頂點數據到緩沖中提供給OpenGL使用 113 glBindBuffer(GL_ARRAY_BUFFER, VBO); 114 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 115 116 //設置頂點屬性指針 117 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0); 118 glEnableVertexAttribArray(0); 119 120 //解綁VBO 121 glBindBuffer(GL_ARRAY_BUFFER, 0); 122 123 //解綁VAO 124 glBindVertexArray(0); 125 126 while (!glfwWindowShouldClose(window)){ 127 glfwPollEvents(); 128 129 //釋放顏色緩存 130 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 131 glClear(GL_COLOR_BUFFER_BIT); 132 133 //繪制物體 134 //當打算渲染一個物體使用使用着色器程序 135 glUseProgram(shaderProgram); 136 glBindVertexArray(VAO); 137 138 //繪制函數 139 glDrawArrays(GL_TRIANGLES, 0, 3); 140 glBindVertexArray(0); 141 142 glfwSwapBuffers(window); 143 } 144 145 glDeleteVertexArrays(1, &VAO); 146 glDeleteBuffers(1, &VBO); 147 148 glfwTerminate(); 149 return 0; 150 } 151 void key_callback(GLFWwindow* window, int key, int scancode, 152 int action, int mode){ 153 if (key == GLFW_KEY_L && action == GLFW_PRESS){ 154 glfwSetWindowShouldClose(window, GL_TRUE); 155 } 156 }
首先,在發該貼的時候,這個程序依舊沒有跑起來,因為GLFW、GLEW等庫的原因,鑒於GLUT是上個時代的產物,所以學到后面看到的一些案例都是用的GLEW、GLFW、GLAD等庫,一時半會兒沒有配置成功,但是,這並不能影響我們根據其中的代碼來理解着色器程序(shader)。
下面,我們主要來看一下其中的着色器代碼部分:
一、兩個着色器程序
const GLchar* vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 position;\n" "void main()\n" "{\n" "gl_Position = vec4(position.x,position.y,position.z,1.0);\n" "}\0"; const GLchar* fragmentShaderSource = "#version 330 core\n" "out vec4 color;\n" "void main()\n" "{\n" "color = vec4(1.0f,0.5f,0.2f,1.0f);\n" "}\n\0";
首先第一個是頂點着色器(vertexShader):
頂點着色器用於讀取頂點(坐標)數據,所以這個position參數是從外部數據源讀取的,在main方法中將外部讀取的頂點數據轉化為四維坐標(x,y,z,w),並且賦值給全局變量:gl_Position。
第二個是片元着色器(fragmentShader):
這里注意了,片元着色器隱式地對所有的gl_Position中的坐標點進行着色並且將顏色輸出。所以這個color參數是輸出的,可能你也看到了,輸出的顏色是個vec4,分別代表RGBA,最后一個1.0f表示alpha通道值為1.0f(浮點型)
二、着色器程序的編譯
//定點着色器 GLuint vertextShader; vertextShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertextShader, 1, &vertexShaderSource, NULL); glCompileShader(vertextShader); //用於判斷一個着色器是否編譯成功 GLint success; GLchar infoLog[512]; glGetShaderiv(vertextShader, GL_COMPILE_STATUS, &success); if (!success){ glGetShaderInfoLog(vertextShader, 512, NULL, infoLog); cout << "vertextShader COMPILE FAILED" << endl; } //片元着色器 GLuint fragmentShader; 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); cout << "fragmentShader COMPILE FAILED" << endl; }
這一塊也不難理解:
1st,定義一個着色器(頂點着色器或者片元着色器);
2nd,為這個着色器對象加載着色器程序片段;
3rd,編譯這個着色器;
當然,案例程序還加了一段編譯成功與否的判斷,這是有必要的,方便調試。
三、多個着色器連接
//創建一個着色器程序對象:多個着色器最后鏈接的版本 GLuint shaderProgram; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertextShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success){ glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); } glDeleteShader(vertextShader); glDeleteShader(fragmentShader);
這塊也不難理解,新建一個shaderProgram,並且通過glAttachShader() API將之前的兩個着色器進行連接,這樣頂點着色器輸出的四維坐標就能供片元着色器使用了。
兩個着色器進行連接之后,對之前的着色器進行刪除。
四、將外部頂點數據進行綁定(用戶自定義數據)
//創建定點數據 GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; //創建 VBO 頂點緩沖數據,並把數據賦值到內存中 GLuint VBO; glGenBuffers(1, &VBO); //VAO頂點數組對象 GLuint VAO; glGenVertexArrays(1, &VAO); //綁定VAO glBindVertexArray(VAO); //復制頂點數據到緩沖中提供給OpenGL使用 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //設置頂點屬性指針 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0); glEnableVertexAttribArray(0); //解綁VBO glBindBuffer(GL_ARRAY_BUFFER, 0); //解綁VAO glBindVertexArray(0);
不難看出,用戶自定義頂點數據為三個頂點(-0.5,-0.5,0)(0.5,-0.5,0)和(0,0.5,0);
新建VAO、VBO對象並將其放到內存中;
將用戶自定義數據賦值到VBO中;
后面的VAO相關操作沒有看懂,這一塊不是直接用VBO(頂點緩存對象)么,並沒有用到VAO啊???
glVertexAttribPointer()我的理解就是為這些頂點(坐標)設置相應的指針好讓程序知道如何操作。
五、渲染
//釋放顏色緩存 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //繪制物體 //當打算渲染一個物體使用使用着色器程序 glUseProgram(shaderProgram); glBindVertexArray(VAO); //繪制函數 glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glfwSwapBuffers(window);
首先,清空屏幕;
然后調用之前的shaderProgram,並為其綁定數據(??不是已經綁定數據了么)