學習自:
https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_7
首先放一張效果圖:
本次教程,將着色器單獨定義了一個類,方便代碼閱讀與編寫。
1,首先新建要給shader類:shader_s.h
(1)shader_s.h
1 #ifndef SHADER_H 2 #define SHADER_H 3 4 #include <glad/glad.h> // 包含glad來獲取所有的必須OpenGL頭文件 5 6 #include <string> 7 #include <fstream> 8 #include <sstream> 9 #include <iostream> 10 11 12 class Shader 13 { 14 public: 15 // 程序ID 16 unsigned int ID; 17 18 // 構造器讀取並構建着色器 19 Shader(const GLchar* vertexPath, const GLchar* fragmentPath); 20 // 使用/激活程序 21 void use(); 22 // uniform工具函數 23 void setBool(const std::string &name, bool value) const; 24 void setInt(const std::string &name, int value) const; 25 void setFloat(const std::string &name, float value) const; 26 private: 27 void checkCompileErrors(unsigned int shader, std::string type); 28 }; 29 30 #endif
(2)將頭文件中的方法逐一實現
注意的是,我們類的寫法,與鏈接中的寫法有不同之處。
#include "shader_s.h" Shader::Shader(const GLchar * vertexPath, const GLchar * fragmentPath) { // 1. 從文件路徑中獲取頂點/片段着色器 std::string vertexCode; std::string fragmentCode; std::ifstream vShaderFile; std::ifstream fShaderFile; // 保證ifstream對象可以拋出異常: vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { // 打開文件 vShaderFile.open(vertexPath); fShaderFile.open(fragmentPath); std::stringstream vShaderStream, fShaderStream; // 讀取文件的緩沖內容到數據流中 vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); // 關閉文件處理器 vShaderFile.close(); fShaderFile.close(); // 轉換數據流到string vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); } catch (std::ifstream::failure e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; } const char* vShaderCode = vertexCode.c_str(); const char * fShaderCode = fragmentCode.c_str(); // 2. 編譯着色器 unsigned int vertex, fragment; // 頂點着色器 vs vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vShaderCode, NULL); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); // 片段着色器 fs fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fShaderCode, NULL); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); // 着色器程序 ID = glCreateProgram(); glAttachShader(ID, vertex); glAttachShader(ID, fragment); glLinkProgram(ID); checkCompileErrors(ID, "PROGRAM"); // 刪除着色器,它們已經鏈接到我們的程序中了,已經不再需要了 glDeleteShader(vertex); glDeleteShader(fragment); } void Shader::use() { glUseProgram(ID); } void Shader::setBool(const std::string & name, bool value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); } void Shader::setInt(const std::string & name, int value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), value); } void Shader::setFloat(const std::string & name, float value) const { glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } void Shader::checkCompileErrors(unsigned int shader, std::string type) { int success; char infoLog[1024]; if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } }
2,主程序的使用
注意,我們的50行,在使用類的構造方法時候,傳入的兩個字符串表示的是我們的兩個本地文件地址,這兩個文件,表示的是,我們的兩個shader腳本,需要我們的手動創建,並且加入代碼。
兩個腳本在最后給出。
(1)主程序TestShader.cpp
有一點需要注意的是,在vs中,我們自定義的類,如果要引用,用的是雙引號 “”
也就是 #include "shader_s.h" 跟鏈接中的也有區別
1 #include <glad/glad.h> 2 #include <GLFW/glfw3.h> 3 4 #include "shader_s.h" 5 6 #include <iostream> 7 8 void framebuffer_size_callback(GLFWwindow* window, int width, int height); 9 void processInput(GLFWwindow *window); 10 11 // settings 12 const unsigned int SCR_WIDTH = 800; 13 const unsigned int SCR_HEIGHT = 600; 14 15 int main() 16 { 17 // glfw: initialize and configure 18 // ------------------------------ 19 glfwInit(); 20 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 21 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 22 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 23 24 #ifdef __APPLE__ 25 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X 26 #endif 27 28 // glfw window creation 29 // -------------------- 30 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); 31 if (window == NULL) 32 { 33 std::cout << "Failed to create GLFW window" << std::endl; 34 glfwTerminate(); 35 return -1; 36 } 37 glfwMakeContextCurrent(window); 38 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 39 40 // glad: load all OpenGL function pointers 41 // --------------------------------------- 42 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 43 { 44 std::cout << "Failed to initialize GLAD" << std::endl; 45 return -1; 46 } 47 48 // build and compile our shader program 49 // ------------------------------------ 50 Shader ourShader("../res/shader.vs", "../res/shader.fs"); // you can name your shader files however you like 51 52 // set up vertex data (and buffer(s)) and configure vertex attributes 53 // ------------------------------------------------------------------ 54 float vertices[] = { 55 // 位置信息 // 顏色信息 56 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 57 -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 58 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 頂部 59 }; 60 61 unsigned int VBO, VAO; 62 glGenVertexArrays(1, &VAO); 63 glGenBuffers(1, &VBO); 64 //首先綁定頂點數組對象,然后綁定並設置頂點緩沖區,然后配置頂點屬性。 65 glBindVertexArray(VAO); 66 67 glBindBuffer(GL_ARRAY_BUFFER, VBO); 68 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 69 70 // 位置屬性 71 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); 72 glEnableVertexAttribArray(0); 73 // 顏色屬性 74 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); 75 glEnableVertexAttribArray(1); 76 /* 77 glVertexAttribPointer 指定了渲染時索引值為 index 的頂點屬性數組的數據格式和位置。 78 void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,const GLvoid * pointer); 79 參數: 80 (1)index 81 指定要修改的頂點屬性的索引值 82 (2)size 83 指定每個頂點屬性的組件數量。必須為1、2、3或者4。初始值為4。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a)) 84 (4)type 85 指定數組中每個組件的數據類型。可用的符號常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。 86 (5)normalized 87 指定當被訪問時,固定點數據值是否應該被歸一化(GL_TRUE)或者直接轉換為固定點值(GL_FALSE)。 88 (6)stride 89 指定連續頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0。 90 (7)pointer 91 指定第一個組件在數組的第一個頂點屬性中的偏移量。該數組與GL_ARRAY_BUFFER綁定,儲存於緩沖區中。初始值為0; 92 */ 93 94 // 循環渲染 95 while (!glfwWindowShouldClose(window)) 96 { 97 // input 98 // ----- 99 processInput(window); 100 101 // render 102 // ------ 103 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 104 glClear(GL_COLOR_BUFFER_BIT); 105 106 // render the triangle 107 ourShader.use(); 108 glBindVertexArray(VAO); 109 glDrawArrays(GL_TRIANGLES, 0, 3); 110 111 // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) 112 // ------------------------------------------------------------------------------- 113 glfwSwapBuffers(window); 114 glfwPollEvents(); 115 } 116 117 // optional: de-allocate all resources once they've outlived their purpose: 118 // ------------------------------------------------------------------------ 119 glDeleteVertexArrays(1, &VAO); 120 glDeleteBuffers(1, &VBO); 121 122 // glfw: terminate, clearing all previously allocated GLFW resources. 123 // ------------------------------------------------------------------ 124 glfwTerminate(); 125 return 0; 126 } 127 128 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly 129 // --------------------------------------------------------------------------------------------------------- 130 void processInput(GLFWwindow *window) 131 { 132 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 133 glfwSetWindowShouldClose(window, true); 134 } 135 136 // glfw: whenever the window size changed (by OS or user resize) this callback function executes 137 // --------------------------------------------------------------------------------------------- 138 void framebuffer_size_callback(GLFWwindow* window, int width, int height) 139 { 140 // make sure the viewport matches the new window dimensions; note that width and 141 // height will be significantly larger than specified on retina displays. 142 glViewport(0, 0, width, height); 143 }
(2) 腳本編寫
TestShader是我的項目名,然后新建文件夾res,存放我們的腳本,然后創建兩個腳本,名字自己取就好,主程序中對應好就好了。
a)頂點着色器(Vetex Shader)shader.vs
#version 330 core layout (location = 0) in vec3 aPos; // 位置變量的屬性位置值為 0 layout (location = 1) in vec3 aColor; // 顏色變量的屬性位置值為 1 out vec3 ourColor; // 向片段着色器輸出一個顏色 void main() { gl_Position = vec4(aPos, 1.0); ourColor = aColor; // 將ourColor設置為我們從頂點數據那里得到的輸入顏色 }
b)片段着色器(fragment Shader)shader.fs
1 #version 330 core 2 out vec4 FragColor; 3 in vec3 ourColor; 4 5 void main() 6 { 7 FragColor = vec4(ourColor, 1.0); 8 }
3,最后運行我們的程序,就可以得到下面的效果圖: