在观察空间(而不是世界空间)中计算冯氏光照:
1 // Vertex shader: 2 // ================ 3 #version 330 core 4 layout (location = 0) in vec3 aPos; 5 layout (location = 1) in vec3 aNormal; 6 7 out vec3 FragPos; 8 out vec3 Normal; 9 out vec3 LightPos; 10 11 uniform vec3 lightPos; // we now define the uniform in the vertex shader and pass the 'view space' lightpos to the fragment shader. lightPos is currently in world space. 12 13 uniform mat4 model; 14 uniform mat4 view; 15 uniform mat4 projection; 16 17 void main() 18 { 19 gl_Position = projection * view * model * vec4(aPos, 1.0); 20 FragPos = vec3(view * model * vec4(aPos, 1.0)); 21 Normal = mat3(transpose(inverse(view * model))) * aNormal; 22 LightPos = vec3(view * vec4(lightPos, 1.0)); // Transform world-space light position to view-space light position 23 } 24 25 26 // Fragment shader: 27 // ================ 28 #version 330 core 29 out vec4 FragColor; 30 31 in vec3 FragPos; 32 in vec3 Normal; 33 in vec3 LightPos; // extra in variable, since we need the light position in view space we calculate this in the vertex shader 34 35 uniform vec3 lightColor; 36 uniform vec3 objectColor; 37 38 void main() 39 { 40 // ambient 41 float ambientStrength = 0.1; 42 vec3 ambient = ambientStrength * lightColor; 43 44 // diffuse 45 vec3 norm = normalize(Normal); 46 vec3 lightDir = normalize(LightPos - FragPos); 47 float diff = max(dot(norm, lightDir), 0.0); 48 vec3 diffuse = diff * lightColor; 49 50 // specular 51 float specularStrength = 0.5; 52 vec3 viewDir = normalize(-FragPos); // the viewer is always at (0,0,0) in view-space, so viewDir is (0,0,0) - Position => -Position 53 vec3 reflectDir = reflect(-lightDir, norm); 54 float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); 55 vec3 specular = specularStrength * spec * lightColor; 56 57 vec3 result = (ambient + diffuse + specular) * objectColor; 58 FragColor = vec4(result, 1.0); 59 }
注意:
- 在观察空间计算的好处是,观察者的位置总是(0, 0, 0),所以这样你直接就获得了观察者位置。可是我发现在学习的时候在世界空间中计算光照更符合直觉。如果你仍然希望在观察空间计算光照的话,你需要将所有相关的向量都用观察矩阵进行变换(记得也要改变法线矩阵)。
- 当我们讨论摄像机/观察空间(Camera/View Space)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标:观察矩阵(LookAt)把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。
- LookAt矩阵就像它的名字表达的那样:它会创建一个看着(Look at)给定目标的观察矩阵。
1 #include <glad/glad.h> 2 #include <GLFW/glfw3.h> 3 #define STB_IMAGE_IMPLEMENTATION 4 #include <stb/stb_image.h> 5 6 #include <glm/glm.hpp> 7 #include <glm/gtc/matrix_transform.hpp> 8 #include <glm/gtc/type_ptr.hpp> 9 10 #include <Shader/shader.h> 11 #include <Camera/camera.h> 12 13 #include <iostream> 14 15 void framebuffer_size_callback(GLFWwindow* window, int width, int height); 16 void mouse_callback(GLFWwindow* window, double xpos, double ypos); 17 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); 18 void processInput(GLFWwindow *window); 19 20 // settings 21 const unsigned int SCR_WIDTH = 800; 22 const unsigned int SCR_HEIGHT = 600; 23 24 // camera 25 Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); 26 float lastX = SCR_WIDTH / 2.0f; 27 float lastY = SCR_HEIGHT / 2.0f; 28 bool firstMouse = true; 29 30 //timeing 31 float deltaTime = 0.0f; // 当前帧与上一帧的时间差 32 float lastFrame = 0.0f; // 上一帧的时间 33 34 // lighting 35 glm::vec3 lightPos(1.2f, 1.0f, 2.0f); 36 37 int main() 38 { 39 // glfw: initialize and configure 40 // ------------------------------ 41 glfwInit(); 42 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 43 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 44 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 45 46 #ifdef __APPLE__ 47 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X 48 #endif 49 50 // glfw window creation 51 // -------------------- 52 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); 53 if (window == NULL) 54 { 55 std::cout << "Failed to create GLFW window" << std::endl; 56 glfwTerminate(); 57 return -1; 58 } 59 glfwMakeContextCurrent(window); 60 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 61 glfwSetCursorPosCallback(window, mouse_callback); 62 glfwSetScrollCallback(window, scroll_callback); 63 64 // tell GLFW to capture our mouse 65 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 66 67 // glad: load all OpenGL function pointers 68 // --------------------------------------- 69 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 70 { 71 std::cout << "Failed to initialize GLAD" << std::endl; 72 return -1; 73 } 74 75 // configure global opengl state 76 // ----------------------------- 77 glEnable(GL_DEPTH_TEST); 78 79 // build and compile our shader zprogram 80 // ------------------------------------ 81 Shader lightingShader("colors.vs", "colors.fs"); 82 Shader lampShader("lamp.vs", "lamp.fs"); 83 84 // set up vertex data (and buffer(s)) and configure vertex attributes 85 // ------------------------------------------------------------------ 86 float vertices[] = { 87 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 88 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 89 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 90 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 91 -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 92 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 93 94 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 95 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 96 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 97 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 98 -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 99 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 100 101 -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 102 -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 103 -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 104 -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 105 -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 106 -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 107 108 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 109 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 110 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 111 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 112 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 113 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 114 115 -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 116 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 117 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 118 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 119 -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 120 -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 121 122 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 123 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 124 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 125 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 126 -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 127 -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f 128 }; 129 130 unsigned int VBO, cubeVAO; 131 glGenVertexArrays(1, &cubeVAO); 132 glGenBuffers(1, &VBO); 133 134 glBindVertexArray(cubeVAO); 135 136 glBindBuffer(GL_ARRAY_BUFFER, VBO); 137 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 138 139 // position attribute 140 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); 141 glEnableVertexAttribArray(0); 142 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); 143 glEnableVertexAttribArray(1); 144 145 unsigned int lightVAO; 146 glGenVertexArrays(1, &lightVAO); 147 glBindVertexArray(lightVAO); 148 // 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据 149 glBindBuffer(GL_ARRAY_BUFFER, VBO); 150 // 设置灯立方体的顶点属性(对我们的灯来说仅仅只有位置数据) 151 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); 152 glEnableVertexAttribArray(0); 153 154 // pass projection matrix to shader (as projection matrix rarely changes there's no need to do this per frame) 155 // ----------------------------------------------------------------------------------------------------------- 156 //glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); 157 //ourShader.setMat4("projection", projection); 158 159 160 // render loop 161 // ----------- 162 while (!glfwWindowShouldClose(window)) 163 { 164 float currentFrame = glfwGetTime(); 165 deltaTime = currentFrame - lastFrame; 166 lastFrame = currentFrame; 167 168 // input 169 // ----- 170 processInput(window); 171 172 // render 173 // ------ 174 glClearColor(0.1f, 0.1f, 0.1f, 1.0f); 175 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 176 177 // activate shader 178 lightingShader.use(); 179 lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f); 180 lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f); 181 lightingShader.setVec3("lightPos", lightPos); 182 lightingShader.setVec3("viewPos", camera.Position); 183 184 // pass projection matrix to shader (note that in this case it could change every frame) 185 glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); 186 lightingShader.setMat4("projection", projection); 187 188 // camera/view transformation 189 glm::mat4 view = camera.GetViewMatrix(); 190 lightingShader.setMat4("view", view); 191 192 glm::mat4 model = glm::mat4(1.0); 193 lightingShader.setMat4("model", model); 194 // render boxes 195 glBindVertexArray(cubeVAO); 196 glDrawArrays(GL_TRIANGLES, 0, 36); 197 198 lampShader.use(); 199 lampShader.setMat4("projection", projection); 200 lampShader.setMat4("view", view); 201 model = glm::mat4(1.0f); 202 float r = 5.0f; 203 float x = sin(glfwGetTime())*r; 204 float z = cos(glfwGetTime())*r; 205 lightPos.x = x; lightPos.z = z; 206 model = glm::translate(model, lightPos); 207 model = glm::scale(model, glm::vec3(0.2f)); 208 lampShader.setMat4("model", model); 209 210 glBindVertexArray(lightVAO); 211 glDrawArrays(GL_TRIANGLES, 0, 36); 212 213 // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) 214 // ------------------------------------------------------------------------------- 215 glfwSwapBuffers(window); 216 glfwPollEvents(); 217 } 218 219 // optional: de-allocate all resources once they've outlived their purpose: 220 // ------------------------------------------------------------------------ 221 glDeleteVertexArrays(1, &cubeVAO); 222 glDeleteVertexArrays(1, &lightVAO); 223 glDeleteBuffers(1, &VBO); 224 225 // glfw: terminate, clearing all previously allocated GLFW resources. 226 // ------------------------------------------------------------------ 227 glfwTerminate(); 228 return 0; 229 } 230 231 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly 232 // --------------------------------------------------------------------------------------------------------- 233 void processInput(GLFWwindow *window) 234 { 235 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 236 glfwSetWindowShouldClose(window, true); 237 238 if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) 239 camera.ProcessKeyboard(FORWARD, deltaTime); 240 if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) 241 camera.ProcessKeyboard(BACKWARD, deltaTime); 242 if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) 243 camera.ProcessKeyboard(LEFT, deltaTime); 244 if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) 245 camera.ProcessKeyboard(RIGHT, deltaTime); 246 } 247 248 // glfw: whenever the window size changed (by OS or user resize) this callback function executes 249 // --------------------------------------------------------------------------------------------- 250 void framebuffer_size_callback(GLFWwindow* window, int width, int height) 251 { 252 // make sure the viewport matches the new window dimensions; note that width and 253 // height will be significantly larger than specified on retina displays. 254 glViewport(0, 0, width, height); 255 } 256 257 void mouse_callback(GLFWwindow* window, double xpos, double ypos){ 258 if (firstMouse) 259 { 260 lastX = xpos; 261 lastY = ypos; 262 firstMouse = false; 263 } 264 265 float xoffset = xpos - lastX; 266 float yoffset = lastY - ypos; 267 //std::cout << ypos << std::endl; 268 lastX = xpos; 269 lastY = ypos; 270 271 camera.ProcessMouseMovement(xoffset, yoffset); 272 } 273 274 // glfw: whenever the mouse scroll wheel scrolls, this callback is called 275 // ---------------------------------------------------------------------- 276 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) 277 { 278 camera.ProcessMouseScroll(yoffset); 279 }
2019/11/29