diff --git a/CMakeLists.txt b/CMakeLists.txt index e9f34b6..2fae736 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,13 +110,13 @@ set(5.advanced_lighting 2.gamma_correction 3.1.shadow_mapping 3.2.point_shadows - 3.3.csm - 4.normal_mapping - 5.parallax_mapping - 6.hdr - 7.bloom - 8.deferred_shading - 9.ssao + # 3.3.csm + # 4.normal_mapping + # 5.parallax_mapping + # 6.hdr + # 7.bloom + # 8.deferred_shading + # 9.ssao ) diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad.vs b/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad.vs new file mode 100644 index 0000000..819c18d --- /dev/null +++ b/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad.vs @@ -0,0 +1,11 @@ +#version 330 core +layout (location = 0) in vec3 position; +layout (location = 1) in vec2 texCoords; + +out vec2 TexCoords; + +void main() +{ + gl_Position = vec4(position, 1.0f); + TexCoords = texCoords; +} \ No newline at end of file diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad_depth.frag b/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad_depth.frag new file mode 100644 index 0000000..38f161d --- /dev/null +++ b/src/5.advanced_lighting/3.1.shadow_mapping/debug_quad_depth.frag @@ -0,0 +1,20 @@ +#version 330 core +out vec4 color; +in vec2 TexCoords; + +uniform sampler2D depthMap; +uniform float near_plane; +uniform float far_plane; + +float LinearizeDepth(float depth) +{ + float z = depth * 2.0 - 1.0; // Back to NDC + return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane)); +} + +void main() +{ + float depthValue = texture(depthMap, TexCoords).r; + // color = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective + color = vec4(vec3(depthValue), 1.0); // orthographic +} \ No newline at end of file diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.cpp b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.cpp index 365b54f..869e033 100644 --- a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.cpp +++ b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.cpp @@ -26,6 +26,9 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void Do_Movement(); GLuint loadTexture(GLchar* path); +void RenderScene(Shader &shader); +void RenderCube(); +void RenderQuad(); // Camera Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); @@ -35,7 +38,11 @@ GLfloat deltaTime = 0.0f; GLfloat lastFrame = 0.0f; // Options -GLboolean blinn = false; +GLboolean shadows = true; + +// Global variables +GLuint woodTexture; +GLuint planeVAO; // The MAIN function, from here we start our application and run our Game loop int main() @@ -71,19 +78,25 @@ int main() // Setup and compile our shaders Shader shader("shadow_mapping.vs", "shadow_mapping.frag"); Shader simpleDepthShader("shadow_mapping_depth.vs", "shadow_mapping_depth.frag"); + Shader debugDepthQuad("debug_quad.vs", "debug_quad_depth.frag"); + + // Set texture samples + shader.Use(); + glUniform1i(glGetUniformLocation(shader.Program, "diffuseTexture"), 0); + glUniform1i(glGetUniformLocation(shader.Program, "shadowMap"), 1); GLfloat planeVertices[] = { - // Positions // Normals // Texture Coords - 8.0f, -0.5f, 8.0f, 0.0f, 1.0f, 0.0f, 5.0f, 0.0f, - -8.0f, -0.5f, 8.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - -8.0f, -0.5f, -8.0f, 0.0f, 1.0f, 0.0f, 0.0f, 5.0f, - - 8.0f, -0.5f, 8.0f, 0.0f, 1.0f, 0.0f, 5.0f, 0.0f, - -8.0f, -0.5f, -8.0f, 0.0f, 1.0f, 0.0f, 0.0f, 5.0f, - 8.0f, -0.5f, -8.0f, 0.0f, 1.0f, 0.0f, 5.0f, 5.0f + // Positions // Normals // Texture Coords + 25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f, + -25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f, + -25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + + 25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f, + 25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 25.0f, + -25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f }; // Setup plane VAO - GLuint planeVAO, planeVBO; + GLuint planeVBO; glGenVertexArrays(1, &planeVAO); glGenBuffers(1, &planeVBO); glBindVertexArray(planeVAO); @@ -98,10 +111,35 @@ int main() glBindVertexArray(0); // Light source - glm::vec3 lightPos(0.0f, 0.0f, 0.0f); + glm::vec3 lightPos(-2.0f, 4.0f, -1.0f); // Load textures - GLuint floorTexture = loadTexture("../../../resources/textures/wood.png"); + woodTexture = loadTexture("../../../resources/textures/wood.png"); + + // Configure depth map FBO + const GLuint SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; + GLuint depthMapFBO; + glGenFramebuffers(1, &depthMapFBO); + // - Create depth texture + GLuint depthMap; + glGenTextures(1, &depthMap); + glBindTexture(GL_TEXTURE_2D, depthMap); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + GLfloat borderColor[] = { 1.0, 1.0, 1.0, 1.0 }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // Game loop while (!glfwWindowShouldClose(window)) @@ -115,30 +153,55 @@ int main() glfwPollEvents(); Do_Movement(); - // Clear the colorbuffer - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Change light position over time + lightPos.z = cos(glfwGetTime()) * 2.0f; - // 1. Render depth of scene to texture (from ligth's perspective) + // 1. Render depth of scene to texture (from light's perspective) // - Get light projection/view matrix. glm::mat4 lightProjection, lightView; - lightProjection = glm::ortho(-10.0, 10.0, -10.0, 10.0, 0.1, 100.0); + glm::mat4 lightSpaceMatrix; + GLfloat near_plane = 1.0f, far_plane = 7.5f; + lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + //lightProjection = glm::perspective(45.0f, (GLfloat)SHADOW_WIDTH / (GLfloat)SHADOW_HEIGHT, near_plane, far_plane); // Note that if you use a perspective projection matrix you'll have to change the light position as the current light position isn't enough to reflect the whole scene. + lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(1.0)); + lightSpaceMatrix = lightProjection * lightView; + // - now render scene from light's point of view + simpleDepthShader.Use(); + glUniformMatrix4fv(glGetUniformLocation(simpleDepthShader.Program, "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix)); + glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glClear(GL_DEPTH_BUFFER_BIT); + RenderScene(simpleDepthShader); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - // Draw objects + // 2. Render scene as normal + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); shader.Use(); - glm::mat4 view = camera.GetViewMatrix(); glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); + glm::mat4 view = camera.GetViewMatrix(); glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); // Set light uniforms glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]); glUniform3fv(glGetUniformLocation(shader.Program, "viewPos"), 1, &camera.Position[0]); - // Floor - glBindVertexArray(planeVAO); - glBindTexture(GL_TEXTURE_2D, floorTexture); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix)); + // Enable/Disable shadows by pressing 'SPACE' + glUniform1i(glGetUniformLocation(shader.Program, "shadows"), shadows); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, woodTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, depthMap); + RenderScene(shader); + // 3. DEBUG: visualize depth map by rendering it to plane + debugDepthQuad.Use(); + glUniform1f(glGetUniformLocation(debugDepthQuad.Program, "near_plane"), near_plane); + glUniform1f(glGetUniformLocation(debugDepthQuad.Program, "far_plane"), far_plane); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, depthMap); + //RenderQuad(); // uncomment this line to see depth map + // Swap the buffers glfwSwapBuffers(window); @@ -148,6 +211,31 @@ int main() return 0; } +void RenderScene(Shader &shader) +{ + // Floor + glm::mat4 model; + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + glBindVertexArray(planeVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + // Cubes + model = glm::mat4(); + model = glm::translate(model, glm::vec3(0.0f, 1.5f, 0.0)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(2.0f, 0.0f, 1.0)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(-1.0f, 0.0f, 2.0)); + model = glm::rotate(model, 60.0f, glm::normalize(glm::vec3(1.0, 0.0, 1.0))); + model = glm::scale(model, glm::vec3(0.5)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); +} + // RenderQuad() Renders a 1x1 quad in NDC, best used for framebuffer color targets // and post-processing effects. @@ -180,6 +268,80 @@ void RenderQuad() glBindVertexArray(0); } +// RenderCube() Renders a 1x1 3D cube in NDC. +GLuint cubeVAO = 0; +GLuint cubeVBO = 0; +void RenderCube() +{ + // Initialize (if necessary) + if (cubeVAO == 0) + { + GLfloat vertices[] = { + // Back face + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // Bottom-left + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,// top-left + // Front face + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + // Left face + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + // Right face + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + // Bottom face + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,// bottom-left + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + // Top face + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left + }; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + // Link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + // Render Cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); +} + // This function loads a texture from file. Note: texture loading functions like these are usually // managed by a 'Resource Manager' that manages all resources (like textures, models, audio). // For learning purposes we'll just define it as a utility function. @@ -221,10 +383,10 @@ void Do_Movement() if (keys[GLFW_KEY_D]) camera.ProcessKeyboard(RIGHT, deltaTime); - if (keys[GLFW_KEY_B] && !keysPressed[GLFW_KEY_B]) + if (keys[GLFW_KEY_SPACE] && !keysPressed[GLFW_KEY_SPACE]) { - blinn = !blinn; - keysPressed[GLFW_KEY_B] = true; + shadows = !shadows; + keysPressed[GLFW_KEY_SPACE] = true; } } diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.frag b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.frag index 2398f4f..3d08cbb 100644 --- a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.frag +++ b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.frag @@ -5,18 +5,60 @@ in VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; + vec4 FragPosLightSpace; } fs_in; -uniform sampler2D floorTexture; +uniform sampler2D diffuseTexture; +uniform sampler2D shadowMap; uniform vec3 lightPos; uniform vec3 viewPos; +uniform bool shadows; + +float ShadowCalculation(vec4 fragPosLightSpace) +{ + // perform perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + // Transform to [0,1] range + projCoords = projCoords * 0.5 + 0.5; + // Get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) + float closestDepth = texture(shadowMap, projCoords.xy).r; + // Get depth of current fragment from light's perspective + float currentDepth = projCoords.z; + // Calculate bias (based on depth map resolution and slope) + vec3 normal = normalize(fs_in.Normal); + vec3 lightDir = normalize(lightPos - fs_in.FragPos); + float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); + // Check whether current frag pos is in shadow + // float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + // PCF + float shadow = 0.0; + vec2 texelSize = 1.0 / textureSize(shadowMap, 0); + for(int x = -1; x <= 1; ++x) + { + for(int y = -1; y <= 1; ++y) + { + float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + } + } + shadow /= 9.0; + + // Keep the shadow at 0.0 when outside the far_plane region of the light's frustum. + if(projCoords.z > 1.0) + shadow = 0.0; + + return shadow; +} + void main() { - vec3 color = texture(floorTexture, fs_in.TexCoords).rgb; + vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; vec3 normal = normalize(fs_in.Normal); - vec3 lightColor = vec3(0.5); + vec3 lightColor = vec3(0.3); + // Ambient + vec3 ambient = 0.3 * color; // Diffuse vec3 lightDir = normalize(lightPos - fs_in.FragPos); float diff = max(dot(lightDir, normal), 0.0); @@ -28,12 +70,9 @@ void main() vec3 halfwayDir = normalize(lightDir + viewDir); spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); vec3 specular = spec * lightColor; - // Simple attenuation - float distance = length(lightPos - fs_in.FragPos); - float attenuation = 1.0 / distance; + // Calculate shadow + float shadow = shadows ? ShadowCalculation(fs_in.FragPosLightSpace) : 0.0; + vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; - diffuse *= attenuation; - specular *= attenuation; - vec3 lighting = (diffuse + specular) * color; FragColor = vec4(lighting, 1.0f); } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.vs b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.vs index 4abade4..b30b1f2 100644 --- a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.vs +++ b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping.vs @@ -9,16 +9,19 @@ out VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; + vec4 FragPosLightSpace; } vs_out; uniform mat4 projection; uniform mat4 view; uniform mat4 model; +uniform mat4 lightSpaceMatrix; void main() { gl_Position = projection * view * model * vec4(position, 1.0f); - vs_out.FragPos = position; + vs_out.FragPos = vec3(model * vec4(position, 1.0)); vs_out.Normal = transpose(inverse(mat3(model))) * normal; vs_out.TexCoords = texCoords; + vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.frag b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.frag index 1a8109c..ffae5f0 100644 --- a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.frag +++ b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.frag @@ -1,7 +1,6 @@ #version 330 core -out vec4 color; void main() { - color = vec4(1.0, 0.0, 0.0, 1.0); + // gl_FragDepth = gl_FragCoord.z; } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.vs b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.vs index 31e024a..f3ed831 100644 --- a/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.vs +++ b/src/5.advanced_lighting/3.1.shadow_mapping/shadow_mapping_depth.vs @@ -1,10 +1,10 @@ #version 330 core layout (location = 0) in vec3 position; -uniform mat4 lightProjectionView; +uniform mat4 lightSpaceMatrix; uniform mat4 model; void main() { - gl_Position = lightProjectionView * model * vec4(position, 1.0f); + gl_Position = lightSpaceMatrix * model * vec4(position, 1.0f); } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.cpp b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.cpp index b51efb8..a778f85 100644 --- a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.cpp +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.cpp @@ -1,6 +1,3 @@ -// Std. Includes -#include - // GLEW #define GLEW_STATIC #include @@ -21,7 +18,7 @@ #include // Properties -GLuint screenWidth = 800, screenHeight = 600; +const GLuint SCR_WIDTH = 800, SCR_HEIGHT = 600; // Function prototypes void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); @@ -29,16 +26,24 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void Do_Movement(); GLuint loadTexture(GLchar* path); +void RenderScene(Shader &shader); +void RenderCube(); +void RenderQuad(); // Camera Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); -bool keys[1024]; -GLfloat lastX = 400, lastY = 300; -bool firstMouse = true; +// Delta GLfloat deltaTime = 0.0f; GLfloat lastFrame = 0.0f; +// Options +GLboolean shadows = true; + +// Global variables +GLuint woodTexture; +GLuint planeVAO; + // The MAIN function, from here we start our application and run our Game loop int main() { @@ -49,7 +54,7 @@ int main() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); - GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed glfwMakeContextCurrent(window); // Set the required callback functions @@ -65,103 +70,55 @@ int main() glewInit(); // Define the viewport dimensions - glViewport(0, 0, screenWidth, screenHeight); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); // Setup some OpenGL options glEnable(GL_DEPTH_TEST); - // glDepthFunc(GL_ALWAYS); // Set to always pass the depth test (same effect as glDisable(GL_DEPTH_TEST)) + glEnable(GL_CULL_FACE); // Setup and compile our shaders - Shader shader("depth_testing.vs", "depth_testing.frag"); + Shader shader("point_shadows.vs", "point_shadows.frag"); + Shader simpleDepthShader("point_shadows_depth.vs", "point_shadows_depth.frag", "point_shadows_depth.gs"); - #pragma region "object_initialization" - // Set the object data (buffers, vertex attributes) - GLfloat cubeVertices[] = { - // Positions // Texture Coords - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, + // Set texture samples + shader.Use(); + glUniform1i(glGetUniformLocation(shader.Program, "diffuseTexture"), 0); + glUniform1i(glGetUniformLocation(shader.Program, "depthMap"), 1); - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f - }; - GLfloat planeVertices[] = { - // Positions // Texture Coords (note we set these higher than 1 that together with GL_REPEAT as texture wrapping mode will cause the floor texture to repeat) - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, 5.0f, 0.0f, 0.0f, - -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, - - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, - 5.0f, -0.5f, -5.0f, 2.0f, 2.0f - }; - // Setup cube VAO - GLuint cubeVAO, cubeVBO; - glGenVertexArrays(1, &cubeVAO); - glGenBuffers(1, &cubeVBO); - glBindVertexArray(cubeVAO); - glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); - glBindVertexArray(0); - // Setup plane VAO - GLuint planeVAO, planeVBO; - glGenVertexArrays(1, &planeVAO); - glGenBuffers(1, &planeVBO); - glBindVertexArray(planeVAO); - glBindBuffer(GL_ARRAY_BUFFER, planeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), &planeVertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); - glBindVertexArray(0); + // Light source + glm::vec3 lightPos(0.0f, 0.0f, 0.0f); // Load textures - GLuint cubeTexture = loadTexture("../../../resources/textures/marble.jpg"); - GLuint floorTexture = loadTexture("../../../resources/textures/metal.png"); - #pragma endregion + woodTexture = loadTexture("../../../resources/textures/wood.png"); + + // Configure depth map FBO + const GLuint SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; + GLuint depthMapFBO; + glGenFramebuffers(1, &depthMapFBO); + // Create depth cubemap texture + GLuint depthCubemap; + glGenTextures(1, &depthCubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); + for (GLuint i = 0; i < 6; ++i) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + // Attach cubemap as depth map FBO's color buffer + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout << "Framebuffer not complete!" << std::endl; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // Game loop - while(!glfwWindowShouldClose(window)) + while (!glfwWindowShouldClose(window)) { // Set frame time GLfloat currentFrame = glfwGetTime(); @@ -172,35 +129,53 @@ int main() glfwPollEvents(); Do_Movement(); - // Clear the colorbuffer - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + // Move light position over time + //lightPos.z = sin(glfwGetTime() * 0.5) * 3.0; + + // 0. Create depth cubemap transformation matrices + GLfloat aspect = (GLfloat)SHADOW_WIDTH / (GLfloat)SHADOW_HEIGHT; + GLfloat near = 1.0f; + GLfloat far = 25.0f; + glm::mat4 shadowProj = glm::perspective(90.0f, aspect, near, far); + std::vector shadowTransforms; + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0))); + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0))); + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0))); + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, -1.0, 0.0), glm::vec3(0.0, 0.0, -1.0))); + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 0.0, 1.0), glm::vec3(0.0, -1.0, 0.0))); + shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 0.0, -1.0), glm::vec3(0.0, -1.0, 0.0))); + + // 1. Render scene to depth cubemap + glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glClear(GL_DEPTH_BUFFER_BIT); + simpleDepthShader.Use(); + for (GLuint i = 0; i < 6; ++i) + glUniformMatrix4fv(glGetUniformLocation(simpleDepthShader.Program, ("shadowTransforms[" + std::to_string(i) + "]").c_str()), 1, GL_FALSE, glm::value_ptr(shadowTransforms[i])); + glUniform1f(glGetUniformLocation(simpleDepthShader.Program, "far_plane"), far); + glUniform3fv(glGetUniformLocation(simpleDepthShader.Program, "lightPos"), 1, &lightPos[0]); + RenderScene(simpleDepthShader); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // 2. Render scene as normal + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Draw objects - shader.Use(); - glm::mat4 model; + shader.Use(); + glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 view = camera.GetViewMatrix(); - glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 100.0f); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); - // Cubes - glBindVertexArray(cubeVAO); - glBindTexture(GL_TEXTURE_2D, cubeTexture); // We omit the glActiveTexture part since TEXTURE0 is already the default active texture unit. (sampler used in fragment is set to 0 as well as default) - model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f)); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 36); - model = glm::mat4(); - model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f)); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 36); - // Floor - glBindVertexArray(planeVAO); - glBindTexture(GL_TEXTURE_2D, floorTexture); - model = glm::mat4(); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); + // Set light uniforms + glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]); + glUniform3fv(glGetUniformLocation(shader.Program, "viewPos"), 1, &camera.Position[0]); + // Enable/Disable shadows by pressing 'SPACE' + glUniform1i(glGetUniformLocation(shader.Program, "shadows"), shadows); + glUniform1f(glGetUniformLocation(shader.Program, "far_plane"), far); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, woodTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); + RenderScene(shader); // Swap the buffers glfwSwapBuffers(window); @@ -210,63 +185,189 @@ int main() return 0; } +void RenderScene(Shader &shader) +{ + // Room cube + glm::mat4 model; + model = glm::scale(model, glm::vec3(10.0)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + glDisable(GL_CULL_FACE); // Note that we disable culling here since we render 'inside' the cube instead of the usual 'outside' which throws off the normal culling methods. + glUniform1i(glGetUniformLocation(shader.Program, "reverse_normals"), 1); // A small little hack to invert normals when drawing cube from the inside so lighting still works. + RenderCube(); + glUniform1i(glGetUniformLocation(shader.Program, "reverse_normals"), 0); // And of course disable it + glEnable(GL_CULL_FACE); + // Cubes + model = glm::mat4(); + model = glm::translate(model, glm::vec3(4.0f, -3.5f, 0.0)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(2.0f, 3.0f, 1.0)); + model = glm::scale(model, glm::vec3(1.5)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(-3.0f, -1.0f, 0.0)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(-1.5f, 1.0f, 1.5)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); + model = glm::mat4(); + model = glm::translate(model, glm::vec3(-1.5f, 2.0f, -3.0)); + model = glm::rotate(model, 60.0f, glm::normalize(glm::vec3(1.0, 0.0, 1.0))); + model = glm::scale(model, glm::vec3(1.5)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + RenderCube(); +} + + +// RenderCube() Renders a 1x1 3D cube in NDC. +GLuint cubeVAO = 0; +GLuint cubeVBO = 0; +void RenderCube() +{ + // Initialize (if necessary) + if (cubeVAO == 0) + { + GLfloat vertices[] = { + // Back face + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // Bottom-left + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,// top-left + // Front face + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left + // Left face + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right + // Right face + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left + // Bottom face + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,// bottom-left + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right + // Top face + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left + }; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + // Link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + // Render Cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); +} + // This function loads a texture from file. Note: texture loading functions like these are usually // managed by a 'Resource Manager' that manages all resources (like textures, models, audio). // For learning purposes we'll just define it as a utility function. GLuint loadTexture(GLchar* path) { - //Generate texture ID and load texture data + // Generate texture ID and load texture data GLuint textureID; glGenTextures(1, &textureID); - int width,height; + int width, height; unsigned char* image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB); // Assign texture to ID glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); - glGenerateMipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); // Parameters - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); SOIL_free_image_data(image); return textureID; } -#pragma region "User input" - +bool keys[1024]; +bool keysPressed[1024]; // Moves/alters the camera positions based on user input void Do_Movement() { // Camera controls - if(keys[GLFW_KEY_W]) + if (keys[GLFW_KEY_W]) camera.ProcessKeyboard(FORWARD, deltaTime); - if(keys[GLFW_KEY_S]) + if (keys[GLFW_KEY_S]) camera.ProcessKeyboard(BACKWARD, deltaTime); - if(keys[GLFW_KEY_A]) + if (keys[GLFW_KEY_A]) camera.ProcessKeyboard(LEFT, deltaTime); - if(keys[GLFW_KEY_D]) + if (keys[GLFW_KEY_D]) camera.ProcessKeyboard(RIGHT, deltaTime); + + if (keys[GLFW_KEY_SPACE] && !keysPressed[GLFW_KEY_SPACE]) + { + shadows = !shadows; + keysPressed[GLFW_KEY_SPACE] = true; + } } +GLfloat lastX = 400, lastY = 300; +bool firstMouse = true; // Is called whenever a key is pressed/released via GLFW void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { - if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); - if(action == GLFW_PRESS) - keys[key] = true; - else if(action == GLFW_RELEASE) - keys[key] = false; + if (key >= 0 && key <= 1024) + { + if (action == GLFW_PRESS) + keys[key] = true; + else if (action == GLFW_RELEASE) + { + keys[key] = false; + keysPressed[key] = false; + } + } } void mouse_callback(GLFWwindow* window, double xpos, double ypos) { - if(firstMouse) + if (firstMouse) { lastX = xpos; lastY = ypos; @@ -274,17 +375,15 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos) } GLfloat xoffset = xpos - lastX; - GLfloat yoffset = lastY - ypos; - + GLfloat yoffset = lastY - ypos; + lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset); -} +} void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessMouseScroll(yoffset); -} - -#pragma endregion \ No newline at end of file +} \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.frag b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.frag index 5f3992f..906507f 100644 --- a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.frag +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.frag @@ -1,16 +1,106 @@ #version 330 core -out vec4 color; +out vec4 FragColor; -float LinearizeDepth(float depth) // Note that this ranges from [0,1] instead of up to 'far plane distance' since we divide by 'far' +in VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; +} fs_in; + +uniform sampler2D diffuseTexture; +uniform samplerCube depthMap; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +uniform float far_plane; +uniform bool shadows; + + +// array of offset direction for sampling +vec3 gridSamplingDisk[20] = vec3[] +( + vec3(1, 1, 1), vec3(1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3(1, 1, -1), vec3(1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3(1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1), + vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, -1, -1), vec3(0, 1, -1) +); + +float ShadowCalculation(vec3 fragPos) { - float near = 0.1; - float far = 100.0; - float z = depth * 2.0 - 1.0; // Back to NDC - return (2.0 * near) / (far + near - z * (far - near)); + // Get vector between fragment position and light position + vec3 fragToLight = fragPos - lightPos; + // Use the fragment to light vector to sample from the depth map + // float closestDepth = texture(depthMap, fragToLight).r; + // It is currently in linear range between [0,1]. Let's re-transform it back to original depth value + // closestDepth *= far_plane; + // Now get current linear depth as the length between the fragment and light position + float currentDepth = length(fragToLight); + // Now test for shadows + // float bias = 0.05; // We use a much larger bias since depth is now in [near_plane, far_plane] range + // float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + // PCF + // float shadow = 0.0; + // float bias = 0.05; + // float samples = 4.0; + // float offset = 0.1; + // for(float x = -offset; x < offset; x += offset / (samples * 0.5)) + // { + // for(float y = -offset; y < offset; y += offset / (samples * 0.5)) + // { + // for(float z = -offset; z < offset; z += offset / (samples * 0.5)) + // { + // float closestDepth = texture(depthMap, fragToLight + vec3(x, y, z)).r; // Use lightdir to lookup cubemap + // closestDepth *= far_plane; // Undo mapping [0;1] + // if(currentDepth - bias > closestDepth) + // shadow += 1.0; + // } + // } + // } + // shadow /= (samples * samples * samples); + float shadow = 0.0; + float bias = 0.15; + int samples = 20; + float viewDistance = length(viewPos - fragPos); + float diskRadius = (1.0 + (viewDistance / far_plane)) / 25.0; + for(int i = 0; i < samples; ++i) + { + float closestDepth = texture(depthMap, fragToLight + gridSamplingDisk[i] * diskRadius).r; + closestDepth *= far_plane; // Undo mapping [0;1] + if(currentDepth - bias > closestDepth) + shadow += 1.0; + } + shadow /= float(samples); + + // Display closestDepth as debug (to visualize depth cubemap) + // FragColor = vec4(vec3(closestDepth / far_plane), 1.0); + + // return shadow; + return shadow; } void main() -{ - float depth = LinearizeDepth(gl_FragCoord.z); - color = vec4(vec3(depth), 1.0f); +{ + vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; + vec3 normal = normalize(fs_in.Normal); + vec3 lightColor = vec3(0.3); + // Ambient + vec3 ambient = 0.3 * color; + // Diffuse + vec3 lightDir = normalize(lightPos - fs_in.FragPos); + float diff = max(dot(lightDir, normal), 0.0); + vec3 diffuse = diff * lightColor; + // Specular + vec3 viewDir = normalize(viewPos - fs_in.FragPos); + vec3 reflectDir = reflect(-lightDir, normal); + float spec = 0.0; + vec3 halfwayDir = normalize(lightDir + viewDir); + spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); + vec3 specular = spec * lightColor; + // Calculate shadow + float shadow = shadows ? ShadowCalculation(fs_in.FragPos) : 0.0; + vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; + + FragColor = vec4(lighting, 1.0f); } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.vs b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.vs index 3abdaa6..04a9ba8 100644 --- a/src/5.advanced_lighting/3.2.point_shadows/point_shadows.vs +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows.vs @@ -1,15 +1,29 @@ #version 330 core layout (location = 0) in vec3 position; -layout (location = 1) in vec2 texCoords; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec2 texCoords; out vec2 TexCoords; -uniform mat4 model; -uniform mat4 view; +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; +} vs_out; + uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform bool reverse_normals; void main() { gl_Position = projection * view * model * vec4(position, 1.0f); - TexCoords = texCoords; + vs_out.FragPos = vec3(model * vec4(position, 1.0)); + if(reverse_normals) // A slight hack to make sure the outer large cube displays lighting from the 'inside' instead of the default 'outside'. + vs_out.Normal = transpose(inverse(mat3(model))) * (-1.0 * normal); + else + vs_out.Normal = transpose(inverse(mat3(model))) * normal; + vs_out.TexCoords = texCoords; } \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.frag b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.frag new file mode 100644 index 0000000..754b8fc --- /dev/null +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.frag @@ -0,0 +1,16 @@ +#version 330 core +in vec4 FragPos; + +uniform vec3 lightPos; +uniform float far_plane; + +void main() +{ + float lightDistance = length(FragPos.xyz - lightPos); + + // map to [0;1] range by dividing by far_plane + lightDistance = lightDistance / far_plane; + + // Write this as modified depth + gl_FragDepth = lightDistance; +} \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.gs b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.gs new file mode 100644 index 0000000..f61800b --- /dev/null +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.gs @@ -0,0 +1,22 @@ +#version 330 core +layout (triangles) in; +layout (triangle_strip, max_vertices=18) out; + +uniform mat4 shadowTransforms[6]; + +out vec4 FragPos; // FragPos from GS (output per emitvertex) + +void main() +{ + for(int face = 0; face < 6; ++face) + { + gl_Layer = face; // built-in variable that specifies to which face we render. + for(int i = 0; i < 3; ++i) // for each triangle's vertices + { + FragPos = gl_in[i].gl_Position; + gl_Position = shadowTransforms[face] * FragPos; + EmitVertex(); + } + EndPrimitive(); + } +} \ No newline at end of file diff --git a/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.vs b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.vs new file mode 100644 index 0000000..09fd856 --- /dev/null +++ b/src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.vs @@ -0,0 +1,9 @@ +#version 330 core +layout (location = 0) in vec3 position; + +uniform mat4 model; + +void main() +{ + gl_Position = model * vec4(position, 1.0); +} \ No newline at end of file