学习自:
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来获取所有的必须OpenGL头文件 5 6 #include <string> 7 #include 8 #include 9 #include 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 #include2 #include 3 4 #include "shader_s.h" 5 6 #include 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,最后运行我们的程序,就可以得到下面的效果图: