本文基于C++语言,描述OpenGL的颜色
前置知识可参考:
笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:
OpenGL中颜色通常数字化为RGB三个通道,根据光的反射定律,一个不透明的物体颜色为:
\[RGB_{result} = RGB_{light} \cdot RGB_{object} \]即,光源颜色与物体颜色相乘就是物体反射的颜色,也就是被看到的颜色
在片段着色器的GLSL中可以简单地实现颜色反射
白色光源照到黄色物体:
FragColor = vec4(vec3(1.0f, 1.0f, 1.0f)*vec3(1.0f, 1.0f, 0.0f), 1.0);
物体反射颜色为黄色:
蓝绿色光源照到黄色物体:
FragColor = vec4(vec3(0.0f, 1.0f, 1.0f)*vec3(1.0f, 1.0f, 0.0f), 1.0);
物体反射颜色为绿色:
生成顶点数据与链接顶点属性,这里使用的是之前立方体的顶点数据
unsigned int lightVAO; glGenVertexArrays(1, &lightVAO); glBindVertexArray(lightVAO); // 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); // 设置灯立方体的顶点属性(对我们的灯来说仅仅只有位置数据) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);
编写顶点着色器GLSL:
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); }
编写片段着色器GLSL:
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0); // 将向量的四个分量全部设置为1.0 }
生成着色器并链接着色器程序:
Shader lightCubeShader("light_cube.vs.glsl", "light_cube.fs.glsl"); ... lightCubeShader.use(); // 设置模型、视图和投影矩阵uniform ... // 绘制灯立方体对象 glBindVertexArray(lightVAO); glDrawArrays(GL_TRIANGLES, 0, 36);
至此,光源已经完成
对于原来的立方体,给其加上光源和物体颜色
在立方体的片段着色器GLSL中:
#version 330 core out vec4 FragColor; uniform vec3 objectColor; uniform vec3 lightColor; void main() { FragColor = vec4(lightColor * objectColor, 1.0); }
向GPU传输数据:
Shader lightingShader("colors.vs.glsl", "colors.fs.glsl"); ... // 在此之前不要忘记首先 use 对应的着色器程序(来设定uniform) lightingShader.use(); lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f); lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
创建后的光照场景如下图:
创建光照场景完整代码如下:
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> #include <math.h> #include "Shader.hpp" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include <glm/glm.hpp> #include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale #include <glm/ext/matrix_clip_space.hpp> // glm::perspective #include <glm/gtc/type_ptr.hpp> //全局变量 glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 10.0f); glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 lightPos(1.2f, 1.0f, 2.0f); // 函数声明 void framebuffer_size_callback(GLFWwindow *window, int width, int height); void process_input(GLFWwindow *window); int main() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); GLFWwindow *window = glfwCreateWindow(800, 600, "color", nullptr, nullptr); if (window == nullptr) { std::cout << "Faild to create window" << std::endl; glfwTerminate(); } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Faild to initialize glad" << std::endl; return -1; } glad_glViewport(0, 0, 800, 600); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //配置项 glEnable(GL_DEPTH_TEST); Shader lightCubeShader("../light_cube.vs.glsl", "../light_cube.fs.glsl"); Shader lightingShader("../colors.vs.glsl", "../colors.fs.glsl"); unsigned int cubeVAO; glGenVertexArrays(1, &cubeVAO); glBindVertexArray(cubeVAO); float vertices[] = { -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, }; unsigned int VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); unsigned int lightCubeVAO; glGenVertexArrays(1, &lightCubeVAO); glBindVertexArray(lightCubeVAO); // 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); // 设置灯立方体的顶点属性(对我们的灯来说仅仅只有位置数据) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); while (!glfwWindowShouldClose(window)) { process_input(window); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); lightingShader.use(); lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f); lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f); glm::mat4 model = glm::mat4(1.0f); model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); glm::mat4 view = glm::mat4(1.0f); // view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); glm::mat4 projection = glm::mat4(1.0f); projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); // 模型矩阵 int modelLoc = glGetUniformLocation(lightingShader.ID, "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // 观察矩阵和投影矩阵与之类似 int viewLoc = glGetUniformLocation(lightingShader.ID, "view"); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); int projectionLoc = glGetUniformLocation(lightingShader.ID, "projection"); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); // render the cube glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); // also draw the lamp object lightCubeShader.use(); lightCubeShader.setMat4("projection", projection); lightCubeShader.setMat4("view", view); model = glm::mat4(1.0f); model = glm::translate(model, lightPos); model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube lightCubeShader.setMat4("model", model); glBindVertexArray(lightCubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; } void framebuffer_size_callback(GLFWwindow *window, int width, int height) { glViewport(0, 0, width, height); } void process_input(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } float cameraSpeed = 0.05f; // adjust accordingly if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) cameraPos += cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) cameraPos -= cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; }
立方体顶点着色器GLSLcolors.vs.glsl
:
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); }
立方体片段着色器GLSLcolors.fs.glsl
:
#version 330 core out vec4 FragColor; uniform vec3 objectColor; uniform vec3 lightColor; void main() { FragColor = vec4(lightColor * objectColor, 1.0); }
光源顶点着色器GLSLlight_cube.vs.glsl
:
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); }
光源片段着色器GLSLlight_cube.fs.glsl
:
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0); // 将向量的四个分量全部设置为1.0 }
着色器Shader.hpp
:
#ifndef SHADER_HPP #define SHADER_HPP #include <glad/glad.h> #include <glm/glm.hpp> #include <string> #include <fstream> #include <sstream> #include <iostream> class Shader { public: unsigned int ID; // constructor generates the shader on the fly // ------------------------------------------------------------------------ Shader(const char* vertexPath, const char* fragmentPath) { // 1. retrieve the vertex/fragment source code from filePath std::string vertexCode; std::string fragmentCode; std::ifstream vShaderFile; std::ifstream fShaderFile; // ensure ifstream objects can throw exceptions: vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); try { // open files vShaderFile.open(vertexPath); fShaderFile.open(fragmentPath); std::stringstream vShaderStream, fShaderStream; // read file's buffer contents into streams vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); // close file handlers vShaderFile.close(); fShaderFile.close(); // convert stream into string vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); } catch (std::ifstream::failure& e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << e.what() << std::endl; } const char* vShaderCode = vertexCode.c_str(); const char * fShaderCode = fragmentCode.c_str(); // 2. compile shaders unsigned int vertex, fragment; // vertex shader vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vShaderCode, NULL); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); // fragment Shader fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fShaderCode, NULL); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); // shader Program ID = glCreateProgram(); glAttachShader(ID, vertex); glAttachShader(ID, fragment); glLinkProgram(ID); checkCompileErrors(ID, "PROGRAM"); // delete the shaders as they're linked into our program now and no longer necessery glDeleteShader(vertex); glDeleteShader(fragment); } // activate the shader // ------------------------------------------------------------------------ void use() const { glUseProgram(ID); } // utility uniform functions // ------------------------------------------------------------------------ void setBool(const std::string &name, bool value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); } // ------------------------------------------------------------------------ void setInt(const std::string &name, int value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), value); } // ------------------------------------------------------------------------ void setFloat(const std::string &name, float value) const { glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } // ------------------------------------------------------------------------ void setVec2(const std::string &name, const glm::vec2 &value) const { glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); } void setVec2(const std::string &name, float x, float y) const { glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y); } // ------------------------------------------------------------------------ void setVec3(const std::string &name, const glm::vec3 &value) const { glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); } void setVec3(const std::string &name, float x, float y, float z) const { glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z); } // ------------------------------------------------------------------------ void setVec4(const std::string &name, const glm::vec4 &value) const { glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); } void setVec4(const std::string &name, float x, float y, float z, float w) const { glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w); } // ------------------------------------------------------------------------ void setMat2(const std::string &name, const glm::mat2 &mat) const { glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); } // ------------------------------------------------------------------------ void setMat3(const std::string &name, const glm::mat3 &mat) const { glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); } // ------------------------------------------------------------------------ void setMat4(const std::string &name, const glm::mat4 &mat) const { glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); } private: // utility function for checking shader compilation/linking errors. // ------------------------------------------------------------------------ void checkCompileErrors(GLuint shader, std::string type) { GLint success; GLchar 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; } } } }; #endif
Shader.hpp
来自:Code Viewer. Source code: includes/learnopengl/shader_m.h[1]颜色 - LearnOpenGL CN (learnopengl-cn.github.io)