From d64194411947889a097ca257b9db81e8e9bb389d Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Sun, 20 Sep 2020 16:45:41 +0200 Subject: [PATCH 1/8] Implement basic sRGB. Include frambuffers and postprocessing. --- imgui.ini | 4 +-- res/shaders/basic.frag | 5 +-- res/shaders/postprocessing.frag | 20 ++++++++++++ res/shaders/postprocessing.vert | 11 +++++++ res/shaders/skybox.frag | 5 ++- src/CMakeLists.txt | 1 + src/Controller.cpp | 58 +++++++++++++++++++++++++-------- src/Controller.h | 6 +++- src/Framebuffer.cpp | 39 ++++++++++++++++++++++ src/Framebuffer.h | 24 ++++++++++++++ src/Light.cpp | 6 ++-- src/Light.h | 10 +++--- src/Texture.cpp | 7 ++-- src/Window.cpp | 3 ++ src/World.h | 2 ++ 15 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 res/shaders/postprocessing.frag create mode 100644 res/shaders/postprocessing.vert create mode 100644 src/Framebuffer.cpp create mode 100644 src/Framebuffer.h diff --git a/imgui.ini b/imgui.ini index ebf8c46..e90789c 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=894,195 Collapsed=0 [Window][Debug Utils] -Pos=39,30 -Size=908,204 +Pos=25,15 +Size=863,335 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 9919065..3bd6e3a 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -109,7 +109,7 @@ vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 view vec3 ambient, diffuse, specular; computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); - return (ambient + diffuse + specular) * 0.5f; + return (ambient + diffuse + specular) * 1.0f; } vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { @@ -181,6 +181,7 @@ void computeShading( float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_c, float K_l, float K_q) { float distanceLightFragment = length(lightPos - fragPos); - return 1.0f / (K_c + K_l * distanceLightFragment + K_q * distanceLightFragment * distanceLightFragment); + //return 1.0f / (K_c + K_l * distanceLightFragment + K_q * distanceLightFragment * distanceLightFragment);# + return 1.0f / (K_q * distanceLightFragment * distanceLightFragment); } diff --git a/res/shaders/postprocessing.frag b/res/shaders/postprocessing.frag new file mode 100644 index 0000000..7fc931b --- /dev/null +++ b/res/shaders/postprocessing.frag @@ -0,0 +1,20 @@ +#version 330 core + +const float GAMMA = 2.0f; + +layout(location = 0) out vec4 f_color; + +in vec2 v_tex_coords; + +uniform sampler2D u_texture; + +void main() { + + vec3 fragmentColor = vec3(texture2D(u_texture, v_tex_coords)); + + // Gamma correction + fragmentColor = pow(fragmentColor, vec3(1.0/GAMMA)); + + f_color = vec4(fragmentColor, 1.0f); + +} \ No newline at end of file diff --git a/res/shaders/postprocessing.vert b/res/shaders/postprocessing.vert new file mode 100644 index 0000000..ecd2527 --- /dev/null +++ b/res/shaders/postprocessing.vert @@ -0,0 +1,11 @@ +#version 330 core + +out vec2 v_tex_coords; + +void main() { + float x = -1.0 + float((gl_VertexID & 1) << 2); + float y = -1.0 + float((gl_VertexID & 2) << 1); + v_tex_coords.x = (x+1.0)*0.5; + v_tex_coords.y = (y+1.0)*0.5; + gl_Position = vec4(x, y, 0, 1); +} \ No newline at end of file diff --git a/res/shaders/skybox.frag b/res/shaders/skybox.frag index ba0bbdd..e13255c 100644 --- a/res/shaders/skybox.frag +++ b/res/shaders/skybox.frag @@ -8,6 +8,9 @@ uniform samplerCube u_skybox; void main() { - f_color = texture(u_skybox, v_texCoord); + + vec3 fragmentColor = vec3(texture(u_skybox, v_texCoord)); + + f_color = vec4(fragmentColor, 1.0f); } \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4723638..a66d9bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(Fall-Fever Entity.cpp Light.cpp World.cpp + Framebuffer.cpp ) target_link_libraries( diff --git a/src/Controller.cpp b/src/Controller.cpp index 6fff68e..f29205e 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -31,7 +31,6 @@ Controller::Controller() { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_SAMPLES, 4); #ifndef _DEBUG glfwWindowHint(GLFW_MAXIMIZED, GL_TRUE); @@ -69,23 +68,25 @@ Controller::~Controller() { delete gameWindow; delete gameEventHandler; delete camera; + delete pp_framebuffer; glfwTerminate(); } void Controller::run() { - glClearColor(0.15f, 0.15f, 0.15f, 1.0f); + glClearColor(0.0015f, 0.0015f, 0.0015f, 1.0f); ShaderProgram shaderProgram("res/shaders/basic.vert", "res/shaders/basic.frag"); ShaderProgram lightProgram("res/shaders/light.vert", "res/shaders/light.frag"); ShaderProgram skyboxProgram("res/shaders/skybox.vert", "res/shaders/skybox.frag"); + ShaderProgram postProcessingProgram("res/shaders/postprocessing.vert", "res/shaders/postprocessing.frag"); - //Model model_backpack("res/models/backpack.ffo"); + Model model_backpack("res/models/backpack.ffo"); //Model model_plant("res/models/plant.ffo"); //Model model_container("res/models/container.ffo"); Model model_cube("res/models/cube.ffo"); Model model_dragon("res/models/dragon.ffo"); - Model model_ground("res/models/ground.ffo"); + Model model_ground("res/models/wood_floor.ffo"); //Model model_moon("res/models/moon.ffo"); //Model model_hut("res/models/hut.ffo"); //Model model_sphere("res/models/sphere.ffo"); @@ -96,7 +97,7 @@ void Controller::run() { //Entity hut(&model_hut, &shaderProgram); //Entity moon(&model_moon, &shaderProgram); //Entity plant(&model_plant, &shaderProgram); - Entity dragon(&model_dragon, &shaderProgram); + Entity dragon(&model_backpack, &shaderProgram); Entity ground(&model_ground, &shaderProgram); Entity lightSource(&model_cube, &lightProgram); @@ -114,6 +115,8 @@ void Controller::run() { camera->translate(glm::vec3(0.0f, 1.5f, 5.0f)); + pp_framebuffer = new Framebuffer(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT); + // This is the game loop while(!glfwWindowShouldClose(gameWindow->getGLFWwindow())) { // Timing @@ -123,29 +126,44 @@ void Controller::run() { // ... static bool rotateLightSource = 0; if(rotateLightSource) { - float radius = 4.0; + float radius = 25.0; glm::vec3 newPos = glm::vec3(-cos(glfwGetTime()*0.5), 0.5f, sin(glfwGetTime()*0.5)) * radius; world.getEntities()->operator[](1).setPosition(newPos); } static glm::vec3 lightColor = glm::vec3(1.f); world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor); - world.updateDirectionalLight(true, glm::vec3(1.0f), lightColor); + world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.001f); lightProgram.bind(); lightProgram.setUniform("v_lightColor", lightColor); lightProgram.unbind(); // Render and buffer swap glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + pp_framebuffer->bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + camera->lookForward(); camera->updateVPM(); skybox.draw(camera->getView(), camera->getProj()); - world.draw(camera->getViewProj(), camera->getPosition()); + pp_framebuffer->unbind(); + + postProcessingProgram.bind(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, pp_framebuffer->getTextureId()); + GLint location = glGetUniformLocation(postProcessingProgram.getShaderProgramId(), "u_texture"); + glUniform1i(location, 0); + GLuint temp_vao; + glGenVertexArrays(1, &temp_vao); + glBindVertexArray(temp_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); + postProcessingProgram.unbind(); #ifdef _DEBUG - renderImGui(world.getEntities(), &lightColor, &rotateLightSource); + renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource); #endif glfwSwapBuffers(gameWindow->getGLFWwindow()); @@ -196,10 +214,13 @@ void Controller::error_callback(int error, const char* description) { void Controller::updateWindowSize() { camera->updateAspectRatio(gameWindow->getWindowAspectRatio()); gameEventHandler->setFirstMouseInput(1); + + delete pp_framebuffer; + pp_framebuffer = new Framebuffer(gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); } #ifdef _DEBUG -void Controller::renderImGui(std::vector *entites, glm::vec3 *lightColor, bool *rotateLightSource) { +void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -210,18 +231,27 @@ void Controller::renderImGui(std::vector *entites, glm::vec3 *lightColor ImGui::Text("Dragon"); static float rotation = 0.0; ImGui::SliderFloat("Rotation", &rotation, 0, 2 * M_PI); - static float translation[] = {0.0f, 0.0f}; - ImGui::SliderFloat2("Position", translation, -4.0, 4.0); + static float translation[] = {0.0f, 0.0f, 0.0f}; + ImGui::SliderFloat3("Position", translation, -4.0, 4.0); static float scale = 0.2f; ImGui::SliderFloat("Scale", &scale, 0.02, 2.0); - entites->operator[](0).setPosition(glm::vec3(translation[0], 0.0f, translation[1])); + entites->operator[](0).setPosition(glm::vec3(translation[0], translation[1], translation[2])); entites->operator[](0).setRotation(glm::vec3(0.f,1.0f,0.f), rotation); entites->operator[](0).setScale(scale); // color picker - ImGui::Text("Light Source"); + ImGui::Text("\nLight Source"); + ImGui::Text("Attenuation"); + static float K_c = 1.0f, K_l = 0.09f, K_q = 0.062f; + ImGui::SliderFloat("Constant Term", &K_c, 0, 1.0f*5); + ImGui::SliderFloat("Linear Term", &K_l, 0, 0.09f*5); + ImGui::SliderFloat("Quadratic Term", &K_q, 0, 0.032f*10); + + pointLight->setParameters(K_c, K_l, K_q); + static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + ImGui::Text("Color"); ImGui::ColorEdit3("Color", color); lightColor->x = color[0]; lightColor->y = color[1]; diff --git a/src/Controller.h b/src/Controller.h index dfcf5e3..f0d5aa3 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -8,6 +8,8 @@ #include "ShaderProgram.h" #include "Entity.h" #include "defines.h" +#include "Light.h" +#include "Framebuffer.h" class Controller { @@ -26,12 +28,14 @@ private: void updateWindowSize(); - void renderImGui(std::vector *entites, glm::vec3 *lightColor, bool *rotateLightSource); + void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource); Window *gameWindow; EventHandler *gameEventHandler; Camera *camera; + Framebuffer *pp_framebuffer; + const uint16_t MAX_FPS = 60; double deltaTime; diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp new file mode 100644 index 0000000..a84a091 --- /dev/null +++ b/src/Framebuffer.cpp @@ -0,0 +1,39 @@ +#include "Framebuffer.h" + +#include + +Framebuffer::Framebuffer(uint32_t width, uint32_t height) { + + glGenFramebuffers(1, &FBO); + + glGenTextures(2, textures); + + glBindTexture(GL_TEXTURE_2D, textures[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, textures[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + bind(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textures[1], 0); + unbind(); +} + +Framebuffer::~Framebuffer() { + glDeleteFramebuffers(1, &FBO); + glDeleteTextures(2, textures); +} + +void Framebuffer::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, FBO); +} + +void Framebuffer::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} diff --git a/src/Framebuffer.h b/src/Framebuffer.h new file mode 100644 index 0000000..bef9ff5 --- /dev/null +++ b/src/Framebuffer.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class Framebuffer { + +public: + + Framebuffer(uint32_t width, uint32_t height); + ~Framebuffer(); + + void bind(); + void unbind(); + + GLuint getTextureId() { + return textures[0]; + } + +private: + + GLuint FBO; + GLuint textures[2]; + +}; \ No newline at end of file diff --git a/src/Light.cpp b/src/Light.cpp index 8c745ef..466b6ac 100644 --- a/src/Light.cpp +++ b/src/Light.cpp @@ -45,9 +45,9 @@ void DirectionalLight::update() { shaderProgram->setUniform("u_directionalLight.isActive", isActive); shaderProgram->setUniform("u_directionalLight.direction", direction); - shaderProgram->setUniform("u_directionalLight.ambient", ambientColor * 0.25f); - shaderProgram->setUniform("u_directionalLight.diffuse", diffuseColor * 0.25f); - shaderProgram->setUniform("u_directionalLight.specular", specularColor * 0.25f); + shaderProgram->setUniform("u_directionalLight.ambient", ambientColor); + shaderProgram->setUniform("u_directionalLight.diffuse", diffuseColor); + shaderProgram->setUniform("u_directionalLight.specular", specularColor); shaderProgram->unbind(); diff --git a/src/Light.h b/src/Light.h index a7770a1..32f4cbc 100644 --- a/src/Light.h +++ b/src/Light.h @@ -19,7 +19,7 @@ public: void setColor(glm::vec3 color) { lightColor = color; diffuseColor = lightColor * glm::vec3(1.0f); - ambientColor = diffuseColor * glm::vec3(0.1f); + ambientColor = diffuseColor * glm::vec3(0.002f); specularColor = lightColor * glm::vec3(1.0f); update(); } @@ -29,6 +29,8 @@ public: update(); } + glm::vec3 getColor() { return lightColor; } + protected: Light() = default; @@ -58,6 +60,8 @@ public: update(); } + void setParameters(float K_c, float K_l, float K_q) { this->K_c = K_c; this->K_l = K_l; this->K_q = K_q; } + void setId(unsigned int id) { lightId = id; } @@ -90,8 +94,6 @@ private: void update() override; - bool isActive = true; - - glm::vec3 direction = glm::vec3(-0.2f, -1.0f, -0.3f); + glm::vec3 direction; }; diff --git a/src/Texture.cpp b/src/Texture.cpp index 13624da..7b762e0 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -28,7 +28,10 @@ Texture::Texture(const char* texturePath, uint8_t textureType) { return; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); + if(textureType == texture_diffuse) + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); //glGenerateMipmap(GL_TEXTURE_2D); stbi_image_free(textureBuffer); @@ -102,7 +105,7 @@ CubeMap::CubeMap(const char* texturePseudoPath) { return; } - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB8_ALPHA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); stbi_image_free(textureBuffer); diff --git a/src/Window.cpp b/src/Window.cpp index feef9d1..86e5c05 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -42,6 +42,9 @@ Window::Window() { #endif setCatchedCursor(mouseCatched); + // Enable primitive gamma correction + // glEnable(GL_FRAMEBUFFER_SRGB); + // Maximize in release build #ifndef _DEBUG glfwMaximizeWindow(window); diff --git a/src/World.h b/src/World.h index 811365f..9763a44 100644 --- a/src/World.h +++ b/src/World.h @@ -21,6 +21,8 @@ public: std::vector * getEntities() { return &entities; } + PointLight * getPointLights() { return pointLights; } + void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); private: From 297735d6a7b1d910eaea84aa65767a74c08e9c11 Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Mon, 21 Sep 2020 09:30:26 +0200 Subject: [PATCH 2/8] Make wireframe mode work again --- src/Controller.cpp | 5 +++++ src/Entity.cpp | 2 +- src/Mesh.cpp | 2 +- src/Mesh.h | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Controller.cpp b/src/Controller.cpp index f29205e..a58e76c 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -155,11 +155,16 @@ void Controller::run() { glBindTexture(GL_TEXTURE_2D, pp_framebuffer->getTextureId()); GLint location = glGetUniformLocation(postProcessingProgram.getShaderProgramId(), "u_texture"); glUniform1i(location, 0); + // Disable wireframe mode + GLint wireframe; + glGetIntegerv(GL_POLYGON_MODE, &wireframe); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GLuint temp_vao; glGenVertexArrays(1, &temp_vao); glBindVertexArray(temp_vao); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); + glPolygonMode(GL_FRONT_AND_BACK, wireframe); postProcessingProgram.unbind(); #ifdef _DEBUG diff --git a/src/Entity.cpp b/src/Entity.cpp index 4a8806c..b98edf6 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -108,7 +108,7 @@ void Skybox::draw(glm::mat4 viewMatrix, glm::mat4 projectionMatrix) { shaderProgram->setUniform("u_viewProjectionMatrix", viewProjectionMatrix); cubeMap.bind(shaderProgram); - cubeModel->getMesh(0)->drawWithoutTextures(shaderProgram); + cubeModel->getMesh(0)->drawWithoutTextures(); cubeMap.unbind(); shaderProgram->unbind(); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a98104e..051c65d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -36,7 +36,7 @@ void Mesh::draw(ShaderProgram *shaderProgram) { } -void Mesh::drawWithoutTextures(ShaderProgram *shaderProgram) { +void Mesh::drawWithoutTextures() { vertexArray.bind(); glDrawElements(GL_TRIANGLES, numElements, GL_UNSIGNED_INT, 0); diff --git a/src/Mesh.h b/src/Mesh.h index 321a2e8..7fdcfd5 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -15,7 +15,7 @@ public: ~Mesh() = default; void draw(ShaderProgram *shaderProgram); - void drawWithoutTextures(ShaderProgram *shaderProgram); + void drawWithoutTextures(); VertexArray * getVertexArray() { return &vertexArray; } From 9dd8ae4018953cda8888e37c8cb96e8a7d2c32d4 Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Mon, 21 Sep 2020 17:41:28 +0200 Subject: [PATCH 3/8] Create Framebuffer render function --- res/shaders/postprocessing.frag | 2 +- src/Controller.cpp | 20 +++++--------------- src/Controller.h | 2 +- src/Framebuffer.cpp | 20 +++++++++++++++++++- src/Framebuffer.h | 7 ++++++- src/World.cpp | 2 +- 6 files changed, 33 insertions(+), 20 deletions(-) diff --git a/res/shaders/postprocessing.frag b/res/shaders/postprocessing.frag index 7fc931b..6dd7047 100644 --- a/res/shaders/postprocessing.frag +++ b/res/shaders/postprocessing.frag @@ -1,6 +1,6 @@ #version 330 core -const float GAMMA = 2.0f; +const float GAMMA = 2.2f; layout(location = 0) out vec4 f_color; diff --git a/src/Controller.cpp b/src/Controller.cpp index f29205e..ffa77e1 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -115,7 +115,7 @@ void Controller::run() { camera->translate(glm::vec3(0.0f, 1.5f, 5.0f)); - pp_framebuffer = new Framebuffer(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT); + pp_framebuffer = new Framebuffer(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, &postProcessingProgram); // This is the game loop while(!glfwWindowShouldClose(gameWindow->getGLFWwindow())) { @@ -150,17 +150,7 @@ void Controller::run() { world.draw(camera->getViewProj(), camera->getPosition()); pp_framebuffer->unbind(); - postProcessingProgram.bind(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, pp_framebuffer->getTextureId()); - GLint location = glGetUniformLocation(postProcessingProgram.getShaderProgramId(), "u_texture"); - glUniform1i(location, 0); - GLuint temp_vao; - glGenVertexArrays(1, &temp_vao); - glBindVertexArray(temp_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); - postProcessingProgram.unbind(); + pp_framebuffer->render(); #ifdef _DEBUG renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource); @@ -170,7 +160,7 @@ void Controller::run() { // Update window size if(gameWindow->checkWindowWasResized()) - updateWindowSize(); + updateWindowSize(&postProcessingProgram); // Check events, handle input gameEventHandler->handleEvents(); @@ -211,12 +201,12 @@ void Controller::error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); } -void Controller::updateWindowSize() { +void Controller::updateWindowSize(ShaderProgram *pp_program) { camera->updateAspectRatio(gameWindow->getWindowAspectRatio()); gameEventHandler->setFirstMouseInput(1); delete pp_framebuffer; - pp_framebuffer = new Framebuffer(gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); + pp_framebuffer = new Framebuffer(gameWindow->getWindowWidth(), gameWindow->getWindowHeight(), pp_program); } #ifdef _DEBUG diff --git a/src/Controller.h b/src/Controller.h index f0d5aa3..b9c5760 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -26,7 +26,7 @@ private: void limit_framerate(); - void updateWindowSize(); + void updateWindowSize(ShaderProgram *pp_program); void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource); diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index a84a091..1cd48d0 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -2,7 +2,9 @@ #include -Framebuffer::Framebuffer(uint32_t width, uint32_t height) { +Framebuffer::Framebuffer(uint32_t width, uint32_t height, ShaderProgram *shaderProgram) { + + this->shaderProgram = shaderProgram; glGenFramebuffers(1, &FBO); @@ -37,3 +39,19 @@ void Framebuffer::bind() { void Framebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } + +void Framebuffer::render() { + shaderProgram->bind(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, getTextureId()); + GLint location = glGetUniformLocation(shaderProgram->getShaderProgramId(), "u_texture"); + glUniform1i(location, 0); + + // A VAO is necessary although no data is stored in it + GLuint temp_vao; + glGenVertexArrays(1, &temp_vao); + glBindVertexArray(temp_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); + shaderProgram->unbind(); +} diff --git a/src/Framebuffer.h b/src/Framebuffer.h index bef9ff5..cb2bb78 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -1,17 +1,20 @@ #pragma once #include +#include "ShaderProgram.h" class Framebuffer { public: - Framebuffer(uint32_t width, uint32_t height); + Framebuffer(uint32_t width, uint32_t height, ShaderProgram *ShaderProgram); ~Framebuffer(); void bind(); void unbind(); + void render(); + GLuint getTextureId() { return textures[0]; } @@ -21,4 +24,6 @@ private: GLuint FBO; GLuint textures[2]; + ShaderProgram *shaderProgram; + }; \ No newline at end of file diff --git a/src/World.cpp b/src/World.cpp index edf570e..2eeaf6c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -20,7 +20,7 @@ World::World(ShaderProgram *shaderProgram) // This will be removed in future when gloss maps are implemented shaderProgram->bind(); - shaderProgram->setUniform("u_material.shininess", 64.0f); + shaderProgram->setUniform("u_material.shininess", 100.0f); shaderProgram->unbind(); } From bbd186bca9561817377f574013eae39faba7d4e7 Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Tue, 22 Sep 2020 10:40:25 +0200 Subject: [PATCH 4/8] Add exposure HDR lighting --- imgui.ini | 2 +- res/shaders/basic.frag | 14 +++----- res/shaders/postprocessing.frag | 5 +++ src/CMakeLists.txt | 1 + src/Camera.h | 7 ++++ src/Controller.cpp | 45 +++++++++++++++-------- src/Controller.h | 5 ++- src/Framebuffer.cpp | 63 ++++++++++++++++++++++++++------- src/Framebuffer.h | 16 +++++++++ src/Light.cpp | 2 -- src/Light.h | 5 ++- src/Shadows.cpp | 24 +++++++++++++ src/Shadows.h | 19 ++++++++++ 13 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 src/Shadows.cpp create mode 100644 src/Shadows.h diff --git a/imgui.ini b/imgui.ini index e90789c..39c660b 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=894,195 Collapsed=0 [Window][Debug Utils] -Pos=25,15 +Pos=37,5 Size=863,335 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 3bd6e3a..2a0032d 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -35,8 +35,6 @@ struct PointLight { bool isActive; vec3 position; - float K_c; - float K_l; float K_q; vec3 ambient; @@ -54,8 +52,6 @@ struct SpotLight { float innerCutOff; float outerCutOff; - float K_c; - float K_l; float K_q; vec3 ambient; @@ -77,7 +73,7 @@ void computeShading( out vec3 ambient, out vec3 diffuse, out vec3 specular ); -float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_c, float K_l, float K_q); +float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q); void main() { @@ -123,7 +119,7 @@ vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 vi vec3 ambient, diffuse, specular; computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); - float attenuation = computeAttenuation(light.position, fragPos, light.K_c, light.K_l, light.K_q); + float attenuation = computeAttenuation(light.position, fragPos, light.K_q); ambient *= attenuation; diffuse *= attenuation; specular *= attenuation; @@ -142,7 +138,7 @@ vec3 spotLightContribution(SpotLight light, vec3 normal, vec3 fragPos, vec3 view vec3 ambient, diffuse, specular; computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); - float attenuation = computeAttenuation(light.position, fragPos, light.K_c, light.K_l, light.K_q); + float attenuation = computeAttenuation(light.position, fragPos, light.K_q); ambient *= attenuation; diffuse *= attenuation; specular *= attenuation; @@ -178,10 +174,10 @@ void computeShading( } -float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_c, float K_l, float K_q) { +float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q) { float distanceLightFragment = length(lightPos - fragPos); - //return 1.0f / (K_c + K_l * distanceLightFragment + K_q * distanceLightFragment * distanceLightFragment);# + return 1.0f / (K_q * distanceLightFragment * distanceLightFragment); } diff --git a/res/shaders/postprocessing.frag b/res/shaders/postprocessing.frag index 6dd7047..dcef925 100644 --- a/res/shaders/postprocessing.frag +++ b/res/shaders/postprocessing.frag @@ -6,12 +6,17 @@ layout(location = 0) out vec4 f_color; in vec2 v_tex_coords; +uniform float u_exposure; + uniform sampler2D u_texture; void main() { vec3 fragmentColor = vec3(texture2D(u_texture, v_tex_coords)); + // Exposure tone mapping + fragmentColor = vec3(1.0) - exp(-fragmentColor * u_exposure); + // Gamma correction fragmentColor = pow(fragmentColor, vec3(1.0/GAMMA)); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a66d9bf..936a4ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(Fall-Fever Light.cpp World.cpp Framebuffer.cpp + Shadows.cpp ) target_link_libraries( diff --git a/src/Camera.h b/src/Camera.h index 52ab4b0..73ca37c 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -1,6 +1,7 @@ #pragma once #include +#include "ShaderProgram.h" class Camera { @@ -17,6 +18,11 @@ public: void translate(glm::vec3 translateVector); void setPosition(glm::vec3 position) { this->position = position; } + void setExposure(ShaderProgram *shaderProgram) { + shaderProgram->setUniform("exposure", exposure); + this->exposure = exposure; + } + void lookAtTarget(glm::vec3 target); void lookForward(); @@ -43,5 +49,6 @@ private: float speed = 2.0f; float fov; + float exposure = 1.0f; }; diff --git a/src/Controller.cpp b/src/Controller.cpp index ffa77e1..2f12890 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -81,6 +81,8 @@ void Controller::run() { ShaderProgram skyboxProgram("res/shaders/skybox.vert", "res/shaders/skybox.frag"); ShaderProgram postProcessingProgram("res/shaders/postprocessing.vert", "res/shaders/postprocessing.frag"); + updateExposure(&postProcessingProgram); + Model model_backpack("res/models/backpack.ffo"); //Model model_plant("res/models/plant.ffo"); //Model model_container("res/models/container.ffo"); @@ -126,15 +128,15 @@ void Controller::run() { // ... static bool rotateLightSource = 0; if(rotateLightSource) { - float radius = 25.0; + float radius = 4.0; glm::vec3 newPos = glm::vec3(-cos(glfwGetTime()*0.5), 0.5f, sin(glfwGetTime()*0.5)) * radius; world.getEntities()->operator[](1).setPosition(newPos); } - static glm::vec3 lightColor = glm::vec3(1.f); - world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor); - world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.001f); + static glm::vec3 lightColor = glm::vec3(1.f); static float intensity = 1.0f; + world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor * intensity); + world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.1f); lightProgram.bind(); - lightProgram.setUniform("v_lightColor", lightColor); + lightProgram.setUniform("v_lightColor", lightColor * 100.0f); lightProgram.unbind(); // Render and buffer swap @@ -146,6 +148,10 @@ void Controller::run() { camera->lookForward(); camera->updateVPM(); + // Calc shadows + // ... + + glViewport(0, 0, gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); skybox.draw(camera->getView(), camera->getProj()); world.draw(camera->getViewProj(), camera->getPosition()); pp_framebuffer->unbind(); @@ -153,7 +159,7 @@ void Controller::run() { pp_framebuffer->render(); #ifdef _DEBUG - renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource); + renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource, &postProcessingProgram, &intensity); #endif glfwSwapBuffers(gameWindow->getGLFWwindow()); @@ -209,8 +215,14 @@ void Controller::updateWindowSize(ShaderProgram *pp_program) { pp_framebuffer = new Framebuffer(gameWindow->getWindowWidth(), gameWindow->getWindowHeight(), pp_program); } +void Controller::updateExposure(ShaderProgram *shaderProgram) { + shaderProgram->bind(); + shaderProgram->setUniform("u_exposure", exposure); + shaderProgram->unbind(); +} + #ifdef _DEBUG -void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource) { +void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -218,7 +230,7 @@ void Controller::renderImGui(std::vector *entites, PointLight *pointLigh // render your GUI ImGui::Begin("Debug Utils"); - ImGui::Text("Dragon"); + ImGui::Text("Object"); static float rotation = 0.0; ImGui::SliderFloat("Rotation", &rotation, 0, 2 * M_PI); static float translation[] = {0.0f, 0.0f, 0.0f}; @@ -232,21 +244,24 @@ void Controller::renderImGui(std::vector *entites, PointLight *pointLigh // color picker ImGui::Text("\nLight Source"); - ImGui::Text("Attenuation"); - static float K_c = 1.0f, K_l = 0.09f, K_q = 0.062f; - ImGui::SliderFloat("Constant Term", &K_c, 0, 1.0f*5); - ImGui::SliderFloat("Linear Term", &K_l, 0, 0.09f*5); - ImGui::SliderFloat("Quadratic Term", &K_q, 0, 0.032f*10); + static float K_q = 0.062f; + ImGui::SliderFloat("Attenuation Parameter", &K_q, 0, 1.5f); - pointLight->setParameters(K_c, K_l, K_q); + updateExposure(postProcessingProgram); + pointLight->setParameters(K_q); static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - ImGui::Text("Color"); + + ImGui::SliderFloat("Intensity", intensity, 0, 50.f); + ImGui::ColorEdit3("Color", color); lightColor->x = color[0]; lightColor->y = color[1]; lightColor->z = color[2]; + ImGui::Text("\nMiscellaneous"); + ImGui::SliderFloat("Exposure", &exposure, 0, 5.0f); + ImGui::Checkbox("Rotate Lightsource", rotateLightSource); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", diff --git a/src/Controller.h b/src/Controller.h index b9c5760..72f1b3b 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -27,8 +27,9 @@ private: void limit_framerate(); void updateWindowSize(ShaderProgram *pp_program); + void updateExposure(ShaderProgram *shaderProgram); - void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource); + void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity); Window *gameWindow; EventHandler *gameEventHandler; @@ -41,4 +42,6 @@ private: bool wireFrameMode = 0; + float exposure = 1.0f; + }; diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index 1cd48d0..fab34f4 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -41,17 +41,56 @@ void Framebuffer::unbind() { } void Framebuffer::render() { - shaderProgram->bind(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, getTextureId()); - GLint location = glGetUniformLocation(shaderProgram->getShaderProgramId(), "u_texture"); - glUniform1i(location, 0); - // A VAO is necessary although no data is stored in it - GLuint temp_vao; - glGenVertexArrays(1, &temp_vao); - glBindVertexArray(temp_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); - shaderProgram->unbind(); + // Disable wireframe mode + GLint wireframe; + glGetIntegerv(GL_POLYGON_MODE, &wireframe); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + shaderProgram->bind(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, getTextureId()); + GLint location = glGetUniformLocation(shaderProgram->getShaderProgramId(), "u_texture"); + glUniform1i(location, 0); + + // A VAO is necessary although no data is stored in it + GLuint temp_vao; + glGenVertexArrays(1, &temp_vao); + glBindVertexArray(temp_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); + + glPolygonMode(GL_FRONT_AND_BACK, wireframe); + shaderProgram->unbind(); +} + + + +DepthMap::DepthMap(int resolution) { + + glGenFramebuffers(1, &depthMapFBO); + + unsigned int depthMap; + glGenTextures(1, &depthMap); + glBindTexture(GL_TEXTURE_2D, depthMap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, resolution, resolution, 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_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + 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); + +} + +void DepthMap::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); +} + +void DepthMap::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/Framebuffer.h b/src/Framebuffer.h index cb2bb78..5d01ed6 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -26,4 +26,20 @@ private: ShaderProgram *shaderProgram; +}; + + +class DepthMap { + +public: + + DepthMap(int resolution); + + void bind(); + void unbind(); + +private: + + GLuint depthMapFBO; + }; \ No newline at end of file diff --git a/src/Light.cpp b/src/Light.cpp index 466b6ac..e9573ba 100644 --- a/src/Light.cpp +++ b/src/Light.cpp @@ -18,8 +18,6 @@ void PointLight::update() { shaderProgram->setUniform((_getStructMemberName() + "ambient").c_str(), ambientColor); shaderProgram->setUniform((_getStructMemberName() + "diffuse").c_str(), diffuseColor); shaderProgram->setUniform((_getStructMemberName() + "specular").c_str(), specularColor); - shaderProgram->setUniform((_getStructMemberName() + "K_c").c_str(), K_c); - shaderProgram->setUniform((_getStructMemberName() + "K_l").c_str(), K_l); shaderProgram->setUniform((_getStructMemberName() + "K_q").c_str(), K_q); shaderProgram->unbind(); diff --git a/src/Light.h b/src/Light.h index 32f4cbc..0e3ba06 100644 --- a/src/Light.h +++ b/src/Light.h @@ -60,7 +60,7 @@ public: update(); } - void setParameters(float K_c, float K_l, float K_q) { this->K_c = K_c; this->K_l = K_l; this->K_q = K_q; } + void setParameters(float K_q) { this->K_q = K_q; } void setId(unsigned int id) { lightId = id; } @@ -72,8 +72,7 @@ private: unsigned int lightId; glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); - float K_c = 1.0f; - float K_l = 0.09f; + float K_q = 0.032f; }; diff --git a/src/Shadows.cpp b/src/Shadows.cpp new file mode 100644 index 0000000..4dc3a54 --- /dev/null +++ b/src/Shadows.cpp @@ -0,0 +1,24 @@ +#include "Shadows.h" + +#include +#include + +Shadows::Shadows() + : depthMap(SHADOW_RES) { + +} + +void Shadows::calculate() { + + glViewport(0, 0, SHADOW_RES, SHADOW_RES); + depthMap.bind(); + glClear(GL_DEPTH_BUFFER_BIT); + // ConfigureShaderAndMatrices(); + float near_plane = 1.0f, far_plane = 7.5f; + glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + glm::mat4 lightView = glm::lookAt(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); + glm::mat4 lightSpaceMatrix = lightProjection * lightView; + // RenderScene(); + depthMap.unbind(); + +} diff --git a/src/Shadows.h b/src/Shadows.h new file mode 100644 index 0000000..bbe6fd7 --- /dev/null +++ b/src/Shadows.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Framebuffer.h" + +class Shadows { + +public: + + Shadows(); + + void calculate(); + +private: + + DepthMap depthMap; + + const int SHADOW_RES = 1024; + +}; \ No newline at end of file From dd44888a4d46a51922671ddfa65928ffe1bffd99 Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Thu, 24 Sep 2020 14:36:57 +0200 Subject: [PATCH 5/8] Basic shadows (not working very well yet) --- imgui.ini | 2 +- res/shaders/basic.frag | 26 +++++++++++++++++++ res/shaders/basic.vert | 4 +++ res/shaders/shadowDepth.frag | 5 ++++ res/shaders/shadowDepth.vert | 11 ++++++++ src/CMakeLists.txt | 1 - src/Controller.cpp | 19 +++++++++----- src/Controller.h | 2 +- src/Entity.cpp | 14 +++++++++++ src/Entity.h | 1 + src/Framebuffer.cpp | 17 +++++++------ src/Framebuffer.h | 5 +++- src/Light.h | 1 + src/Model.cpp | 9 +++++++ src/Model.h | 1 + src/Shadows.cpp | 24 ------------------ src/Shadows.h | 19 -------------- src/World.cpp | 49 +++++++++++++++++++++++++++++++++++- src/World.h | 8 ++++++ 19 files changed, 157 insertions(+), 61 deletions(-) create mode 100644 res/shaders/shadowDepth.frag create mode 100644 res/shaders/shadowDepth.vert delete mode 100644 src/Shadows.cpp delete mode 100644 src/Shadows.h diff --git a/imgui.ini b/imgui.ini index 39c660b..925f5a5 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=894,195 Collapsed=0 [Window][Debug Utils] -Pos=37,5 +Pos=14,6 Size=863,335 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 2a0032d..6d9f1a6 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -5,6 +5,7 @@ layout(location = 0) out vec4 f_color; in vec3 v_normal; in vec2 v_texCoord; in vec3 v_fragmentPosition; +in vec4 v_fragmentPositionLightSpace; struct Material { sampler2D texture_diffuse0; @@ -63,6 +64,8 @@ uniform SpotLight u_spotLight; uniform mat3 u_normalMatrix; uniform vec3 u_viewPosition; +uniform sampler2D u_texture_shadowMap; + vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 viewDir); vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); vec3 spotLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); @@ -75,6 +78,8 @@ void computeShading( float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q); +float computeShadows(vec4 fragPosLightSpace); + void main() { vec3 fragmentColor = vec3(0.0f); @@ -88,6 +93,10 @@ void main() { fragmentColor += pointLightContribution(u_pointLight[i], normal, v_fragmentPosition, viewDir); } + // This is not final: ambient lighting should not be affected of shadows. + float shadows = computeShadows(v_fragmentPositionLightSpace); + fragmentColor *= (1.0f - shadows); + //fragmentColor += spotLightContribution(u_spotLight, normal, v_fragmentPosition, viewDir); f_color = vec4(fragmentColor, 1.0f); @@ -181,3 +190,20 @@ float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q) { return 1.0f / (K_q * distanceLightFragment * distanceLightFragment); } + +float computeShadows(vec4 fragPosLightSpace) { + + // Perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + // Transform from [-1,1] to [0,1] + projCoords *= 0.5f; + projCoords += 0.5f; + + float closestDepth = texture(u_texture_shadowMap, projCoords.xy).r; + float currentDepth = projCoords.z; + + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + + return shadow; +} diff --git a/res/shaders/basic.vert b/res/shaders/basic.vert index 4f13209..b4af7cd 100644 --- a/res/shaders/basic.vert +++ b/res/shaders/basic.vert @@ -8,14 +8,18 @@ out vec3 v_normal; out vec2 v_texCoord; out vec3 v_fragmentPosition; +out vec4 v_fragmentPositionLightSpace; uniform mat4 u_modelViewProjMatrix; uniform mat4 u_modelMatrix; +uniform mat4 u_lightViewProjMatrix; + void main() { gl_Position = u_modelViewProjMatrix * vec4(a_position, 1.0f); v_fragmentPosition = vec3(u_modelMatrix * vec4(a_position, 1.0f)); + v_fragmentPositionLightSpace = u_lightViewProjMatrix * vec4(v_fragmentPosition, 1.0); v_normal = a_normal; v_texCoord = a_texCoord; diff --git a/res/shaders/shadowDepth.frag b/res/shaders/shadowDepth.frag new file mode 100644 index 0000000..6087418 --- /dev/null +++ b/res/shaders/shadowDepth.frag @@ -0,0 +1,5 @@ +#version 330 core + +void main() { + +} \ No newline at end of file diff --git a/res/shaders/shadowDepth.vert b/res/shaders/shadowDepth.vert new file mode 100644 index 0000000..dec698e --- /dev/null +++ b/res/shaders/shadowDepth.vert @@ -0,0 +1,11 @@ +#version 330 core + +layout(location = 0) in vec3 a_position; + +uniform mat4 u_modelViewProjMatrix; + +void main() { + + gl_Position = u_modelViewProjMatrix * vec4(a_position, 1.0f); + +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 936a4ab..a66d9bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,6 @@ add_executable(Fall-Fever Light.cpp World.cpp Framebuffer.cpp - Shadows.cpp ) target_link_libraries( diff --git a/src/Controller.cpp b/src/Controller.cpp index 2f12890..e1ce563 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -80,6 +80,7 @@ void Controller::run() { ShaderProgram lightProgram("res/shaders/light.vert", "res/shaders/light.frag"); ShaderProgram skyboxProgram("res/shaders/skybox.vert", "res/shaders/skybox.frag"); ShaderProgram postProcessingProgram("res/shaders/postprocessing.vert", "res/shaders/postprocessing.frag"); + ShaderProgram shadowDepthProgram("res/shaders/shadowDepth.vert", "res/shaders/shadowDepth.frag"); updateExposure(&postProcessingProgram); @@ -126,13 +127,14 @@ void Controller::run() { // Update game // ... - static bool rotateLightSource = 0; + static bool rotateLightSource = false; if(rotateLightSource) { float radius = 4.0; glm::vec3 newPos = glm::vec3(-cos(glfwGetTime()*0.5), 0.5f, sin(glfwGetTime()*0.5)) * radius; world.getEntities()->operator[](1).setPosition(newPos); } - static glm::vec3 lightColor = glm::vec3(1.f); static float intensity = 1.0f; + static glm::vec3 lightColor = glm::vec3(1.f); + static float intensity = 20.0f; world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor * intensity); world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.1f); lightProgram.bind(); @@ -154,12 +156,16 @@ void Controller::run() { glViewport(0, 0, gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); skybox.draw(camera->getView(), camera->getProj()); world.draw(camera->getViewProj(), camera->getPosition()); - pp_framebuffer->unbind(); + static bool drawShadows = false; + if(drawShadows) + world.calculateShadows(&shadowDepthProgram); + + pp_framebuffer->unbind(); pp_framebuffer->render(); #ifdef _DEBUG - renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource, &postProcessingProgram, &intensity); + renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource, &postProcessingProgram, &intensity, &drawShadows); #endif glfwSwapBuffers(gameWindow->getGLFWwindow()); @@ -222,7 +228,7 @@ void Controller::updateExposure(ShaderProgram *shaderProgram) { } #ifdef _DEBUG -void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity) { +void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -244,7 +250,7 @@ void Controller::renderImGui(std::vector *entites, PointLight *pointLigh // color picker ImGui::Text("\nLight Source"); - static float K_q = 0.062f; + static float K_q = 1.0f; ImGui::SliderFloat("Attenuation Parameter", &K_q, 0, 1.5f); updateExposure(postProcessingProgram); @@ -262,6 +268,7 @@ void Controller::renderImGui(std::vector *entites, PointLight *pointLigh ImGui::Text("\nMiscellaneous"); ImGui::SliderFloat("Exposure", &exposure, 0, 5.0f); + ImGui::Checkbox("Draw Shadows", drawShadows); ImGui::Checkbox("Rotate Lightsource", rotateLightSource); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", diff --git a/src/Controller.h b/src/Controller.h index 72f1b3b..b5b5a3a 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -29,7 +29,7 @@ private: void updateWindowSize(ShaderProgram *pp_program); void updateExposure(ShaderProgram *shaderProgram); - void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity); + void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows); Window *gameWindow; EventHandler *gameEventHandler; diff --git a/src/Entity.cpp b/src/Entity.cpp index b98edf6..949f254 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -32,6 +32,20 @@ void Entity::draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition) { } +void Entity::drawShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram) { + + p_shaderProgram->bind(); + + glm::mat4 modelViewProj = viewProjMatrix * modelMatrix; + shaderProgram->setUniform("u_modelViewProjMatrix", modelViewProj); + + // Draw the model + model->drawWithoutTextures(); + + p_shaderProgram->unbind(); + +} + void Entity::translate(glm::vec3 vector) { position += vector; updateModelMatrix(); diff --git a/src/Entity.h b/src/Entity.h index 635789e..cdb3b46 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -15,6 +15,7 @@ public: ~Entity() = default; void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); + void drawShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram); void translate(glm::vec3 vector); void rotate(glm::vec3 axis, float radians); diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index fab34f4..1b91a38 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -1,6 +1,7 @@ #include "Framebuffer.h" #include +#include Framebuffer::Framebuffer(uint32_t width, uint32_t height, ShaderProgram *shaderProgram) { @@ -19,6 +20,7 @@ Framebuffer::Framebuffer(uint32_t width, uint32_t height, ShaderProgram *shaderP glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); bind(); @@ -66,24 +68,25 @@ void Framebuffer::render() { -DepthMap::DepthMap(int resolution) { +DepthMap::DepthMap(int RESOLUTION) { glGenFramebuffers(1, &depthMapFBO); - unsigned int depthMap; glGenTextures(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, RESOLUTION, RESOLUTION, 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_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glBindTexture(GL_TEXTURE_2D, 0); + + bind(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + unbind(); } diff --git a/src/Framebuffer.h b/src/Framebuffer.h index 5d01ed6..56e6098 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -33,13 +33,16 @@ class DepthMap { public: - DepthMap(int resolution); + DepthMap(int RESOLUTION); void bind(); void unbind(); + GLuint getDepthMap() { return depthMap; } + private: GLuint depthMapFBO; + GLuint depthMap; }; \ No newline at end of file diff --git a/src/Light.h b/src/Light.h index 0e3ba06..4268a89 100644 --- a/src/Light.h +++ b/src/Light.h @@ -39,6 +39,7 @@ protected: ShaderProgram *shaderProgram; bool isActive = false; + bool shouldCastShadow = true; // Color glm::vec3 lightColor; diff --git a/src/Model.cpp b/src/Model.cpp index 692349f..b43955d 100644 --- a/src/Model.cpp +++ b/src/Model.cpp @@ -28,6 +28,15 @@ void Model::draw(ShaderProgram *shaderProgram) { } +void Model::drawWithoutTextures() { + + // Iterate through every mesh and call the draw function + for(auto it = meshes.begin(); it != meshes.end(); it++) { + (*it)->drawWithoutTextures(); + } + +} + void Model::loadModel(std::string pathToModel) { std::ifstream input(pathToModel, std::ios::in | std::ios::binary); diff --git a/src/Model.h b/src/Model.h index b3c1791..59548b0 100644 --- a/src/Model.h +++ b/src/Model.h @@ -13,6 +13,7 @@ public: ~Model(); void draw(ShaderProgram *shaderProgram); + void drawWithoutTextures(); Mesh * getMesh(unsigned int index) { return meshes[index]; } diff --git a/src/Shadows.cpp b/src/Shadows.cpp deleted file mode 100644 index 4dc3a54..0000000 --- a/src/Shadows.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "Shadows.h" - -#include -#include - -Shadows::Shadows() - : depthMap(SHADOW_RES) { - -} - -void Shadows::calculate() { - - glViewport(0, 0, SHADOW_RES, SHADOW_RES); - depthMap.bind(); - glClear(GL_DEPTH_BUFFER_BIT); - // ConfigureShaderAndMatrices(); - float near_plane = 1.0f, far_plane = 7.5f; - glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - glm::mat4 lightView = glm::lookAt(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); - glm::mat4 lightSpaceMatrix = lightProjection * lightView; - // RenderScene(); - depthMap.unbind(); - -} diff --git a/src/Shadows.h b/src/Shadows.h deleted file mode 100644 index bbe6fd7..0000000 --- a/src/Shadows.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "Framebuffer.h" - -class Shadows { - -public: - - Shadows(); - - void calculate(); - -private: - - DepthMap depthMap; - - const int SHADOW_RES = 1024; - -}; \ No newline at end of file diff --git a/src/World.cpp b/src/World.cpp index 2eeaf6c..d686489 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -7,7 +7,7 @@ // constructors for Light and PointLight have only this reason to exist. World::World(ShaderProgram *shaderProgram) - : shaderProgram(shaderProgram) { + : shaderProgram(shaderProgram), depthMapFBO(SHADOW_RES) { // PointLights for(unsigned int i = 0; i < NUM_POINT_LIGHTS; i++) { @@ -57,8 +57,55 @@ void World::updateDirectionalLight(bool active, glm::vec3 direction, glm::vec3 c void World::draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition) { + // Draw all entities for(auto it = entities.begin(); it != entities.end(); it++) { it->draw(viewProjMatrix, viewPosition); } + // Calculate shadows + // calculateShadows(); + +} + +void World::calculateShadows(ShaderProgram *p_shaderProgram) { + + // Get old viewport dimensions to reset them later... + GLint VIEWPORT[4]; + glGetIntegerv(GL_VIEWPORT, VIEWPORT); + + glViewport(0, 0, SHADOW_RES, SHADOW_RES); + + depthMapFBO.bind(); + + glClear(GL_DEPTH_BUFFER_BIT); + // ConfigureShaderAndMatrices(); + float near_plane = 0.1f, far_plane = 7.5f; + glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + glm::mat4 lightView = glm::lookAt(-glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); + glm::mat4 lightViewProjectionMatrix = lightProjection * lightView; + + // Draw scene from light perspective + // Draw all entities + for(auto it = entities.begin(); it != entities.end(); it++) { + it->drawShadows(lightViewProjectionMatrix, p_shaderProgram); + } + + depthMapFBO.unbind(); + + // Reset viewport size + glViewport(VIEWPORT[0], VIEWPORT[1], VIEWPORT[2], VIEWPORT[3]); + + shaderProgram->bind(); + + // Send lightViewProjMatrix to basic shader + shaderProgram->setUniform("u_lightViewProjMatrix", lightViewProjectionMatrix); + + // Send shadowMap to basic shader + const int textureUnit = TEXTURE_TYPE_NUM_ITEMS * 2; + shaderProgram->setUniform("u_texture_shadowMap", (int)textureUnit); + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_2D, depthMapFBO.getDepthMap()); + + shaderProgram->unbind(); + } diff --git a/src/World.h b/src/World.h index 9763a44..28e8aef 100644 --- a/src/World.h +++ b/src/World.h @@ -5,6 +5,8 @@ #include "Light.h" #include "Camera.h" #include "Entity.h" +#include "ShaderProgram.h" +#include "Framebuffer.h" class World { @@ -24,6 +26,7 @@ public: PointLight * getPointLights() { return pointLights; } void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); + void calculateShadows(ShaderProgram *shaderProgram); private: @@ -31,8 +34,13 @@ private: std::vector entities; + // Lights DirectionalLight directionalLight; PointLight pointLights[NUM_POINT_LIGHTS]; //SpotLight spotLight; + // Shadows + const int SHADOW_RES = 1024; + DepthMap depthMapFBO; + }; From 33dc8014838ea992fe28179505219d65c8d26a8f Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Thu, 24 Sep 2020 16:36:32 +0200 Subject: [PATCH 6/8] Better Shadows --- imgui.ini | 2 +- res/shaders/basic.frag | 29 +++++++++++++++++++++-------- res/shaders/shadowDepth.frag | 2 ++ src/World.cpp | 9 +++++++-- src/World.h | 2 +- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/imgui.ini b/imgui.ini index 925f5a5..d94e65f 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=894,195 Collapsed=0 [Window][Debug Utils] -Pos=14,6 +Pos=13,15 Size=863,335 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 6d9f1a6..14dafe3 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -78,7 +78,7 @@ void computeShading( float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q); -float computeShadows(vec4 fragPosLightSpace); +float computeShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); void main() { @@ -93,10 +93,6 @@ void main() { fragmentColor += pointLightContribution(u_pointLight[i], normal, v_fragmentPosition, viewDir); } - // This is not final: ambient lighting should not be affected of shadows. - float shadows = computeShadows(v_fragmentPositionLightSpace); - fragmentColor *= (1.0f - shadows); - //fragmentColor += spotLightContribution(u_spotLight, normal, v_fragmentPosition, viewDir); f_color = vec4(fragmentColor, 1.0f); @@ -114,7 +110,9 @@ vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 view vec3 ambient, diffuse, specular; computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); - return (ambient + diffuse + specular) * 1.0f; + float shadows = computeShadows(v_fragmentPositionLightSpace, normal, lightDir); + + return (ambient + (1.0f - shadows) * (diffuse + specular)) * 1.0f; } vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { @@ -191,7 +189,7 @@ float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q) { } -float computeShadows(vec4 fragPosLightSpace) { +float computeShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) { // Perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; @@ -200,10 +198,25 @@ float computeShadows(vec4 fragPosLightSpace) { projCoords *= 0.5f; projCoords += 0.5f; + if(projCoords.z > 1.0f) return 0.0f; + float closestDepth = texture(u_texture_shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; - float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); + + float shadow = 0.0; + vec2 texelSize = 1.0 / textureSize(u_texture_shadowMap, 0); + for(int x = -1; x <= 1; ++x) + { + for(int y = -1; y <= 1; ++y) + { + float pcfDepth = texture(u_texture_shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + } + } + shadow /= 9.0; + return shadow; } diff --git a/res/shaders/shadowDepth.frag b/res/shaders/shadowDepth.frag index 6087418..85f2da4 100644 --- a/res/shaders/shadowDepth.frag +++ b/res/shaders/shadowDepth.frag @@ -2,4 +2,6 @@ void main() { + // Empty as we aren't rendering to any color buffer + } \ No newline at end of file diff --git a/src/World.cpp b/src/World.cpp index d686489..af0697f 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -79,17 +79,22 @@ void World::calculateShadows(ShaderProgram *p_shaderProgram) { glClear(GL_DEPTH_BUFFER_BIT); // ConfigureShaderAndMatrices(); - float near_plane = 0.1f, far_plane = 7.5f; + float near_plane = 1.0f, far_plane = 15.0f; glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - glm::mat4 lightView = glm::lookAt(-glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); + glm::mat4 lightView = glm::lookAt(-5.0f * glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); glm::mat4 lightViewProjectionMatrix = lightProjection * lightView; + // Switch face culling (Peter panning) + glCullFace(GL_BACK); + // Draw scene from light perspective // Draw all entities for(auto it = entities.begin(); it != entities.end(); it++) { it->drawShadows(lightViewProjectionMatrix, p_shaderProgram); } + glCullFace(GL_FRONT); + depthMapFBO.unbind(); // Reset viewport size diff --git a/src/World.h b/src/World.h index 28e8aef..8f89b8b 100644 --- a/src/World.h +++ b/src/World.h @@ -40,7 +40,7 @@ private: //SpotLight spotLight; // Shadows - const int SHADOW_RES = 1024; + const int SHADOW_RES = 4096; DepthMap depthMapFBO; }; From 55b8efc6c479e3b63bc265e9ef84313f87660aa1 Mon Sep 17 00:00:00 2001 From: 4VRDriver <44267643+4VRDriver@users.noreply.github.com> Date: Sat, 26 Sep 2020 14:21:02 +0200 Subject: [PATCH 7/8] PointLight Shadows --- imgui.ini | 4 +- res/shaders/basic.frag | 74 ++++++++--- res/shaders/basic.vert | 6 +- ...Depth.frag => directionalShadowDepth.frag} | 2 +- ...Depth.vert => directionalShadowDepth.vert} | 2 +- res/shaders/pointShadowDepth.frag | 17 +++ res/shaders/pointShadowDepth.geom | 25 ++++ res/shaders/pointShadowDepth.vert | 11 ++ res/shaders/postprocessing.frag | 2 +- res/shaders/postprocessing.vert | 2 +- res/shaders/skybox.frag | 3 +- res/shaders/skybox.vert | 2 +- src/Controller.cpp | 43 ++++--- src/Controller.h | 2 +- src/Entity.cpp | 15 ++- src/Entity.h | 7 +- src/Framebuffer.cpp | 47 ++++--- src/Framebuffer.h | 13 +- src/Light.h | 5 +- src/ShaderProgram.cpp | 89 ++++++++----- src/ShaderProgram.h | 4 +- src/Texture.cpp | 22 +++- src/Texture.h | 4 + src/World.cpp | 119 +++++++++++++----- src/World.h | 18 ++- 25 files changed, 399 insertions(+), 139 deletions(-) rename res/shaders/{shadowDepth.frag => directionalShadowDepth.frag} (97%) rename res/shaders/{shadowDepth.vert => directionalShadowDepth.vert} (98%) create mode 100644 res/shaders/pointShadowDepth.frag create mode 100644 res/shaders/pointShadowDepth.geom create mode 100644 res/shaders/pointShadowDepth.vert diff --git a/imgui.ini b/imgui.ini index d94e65f..d3c73fe 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=894,195 Collapsed=0 [Window][Debug Utils] -Pos=13,15 -Size=863,335 +Pos=12,15 +Size=871,365 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 14dafe3..b982362 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -5,7 +5,7 @@ layout(location = 0) out vec4 f_color; in vec3 v_normal; in vec2 v_texCoord; in vec3 v_fragmentPosition; -in vec4 v_fragmentPositionLightSpace; +in vec4 v_fragmentPositionDirectionalLightSpace; struct Material { sampler2D texture_diffuse0; @@ -64,7 +64,15 @@ uniform SpotLight u_spotLight; uniform mat3 u_normalMatrix; uniform vec3 u_viewPosition; -uniform sampler2D u_texture_shadowMap; +uniform sampler2D u_texture_directionalShadowMap; +uniform samplerCube u_texture_pointShadowMap0; +//uniform samplerCube u_texture_pointShadowMap1; +//uniform samplerCube u_texture_pointShadowMap2; +//uniform samplerCube u_texture_pointShadowMap3; + +uniform bool b_drawShadows; + +uniform float pointShadowDepthMapFarPlane; vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 viewDir); vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); @@ -78,7 +86,8 @@ void computeShading( float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q); -float computeShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); +float computeDirectionalShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); +float computePointShadows(vec3 fragPos, vec3 lightPos); void main() { @@ -110,9 +119,12 @@ vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 view vec3 ambient, diffuse, specular; computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); - float shadows = computeShadows(v_fragmentPositionLightSpace, normal, lightDir); + float shadows = 0.0f; + //if(b_drawShadows) + if(false) + shadows = computeDirectionalShadows(v_fragmentPositionDirectionalLightSpace, normal, lightDir); - return (ambient + (1.0f - shadows) * (diffuse + specular)) * 1.0f; + return (ambient + (1.0f - shadows) * (diffuse + specular)); } vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { @@ -131,7 +143,11 @@ vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 vi diffuse *= attenuation; specular *= attenuation; - return (ambient + diffuse + specular); + float shadows = 0.0f; + if(b_drawShadows) + shadows = computePointShadows(fragPos, light.position); + + return (ambient + (1.0f - shadows) * (diffuse + specular)); } vec3 spotLightContribution(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { @@ -189,7 +205,7 @@ float computeAttenuation(vec3 lightPos, vec3 fragPos, float K_q) { } -float computeShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) { +float computeDirectionalShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) { // Perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; @@ -200,23 +216,47 @@ float computeShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) { if(projCoords.z > 1.0f) return 0.0f; - float closestDepth = texture(u_texture_shadowMap, projCoords.xy).r; + float closestDepth = texture(u_texture_directionalShadowMap, projCoords.xy).r; float currentDepth = projCoords.z; float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); + bias *= 0.25f; float shadow = 0.0; - vec2 texelSize = 1.0 / textureSize(u_texture_shadowMap, 0); - for(int x = -1; x <= 1; ++x) - { - for(int y = -1; y <= 1; ++y) - { - float pcfDepth = texture(u_texture_shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; - } + vec2 texelSize = 1.0 / textureSize(u_texture_directionalShadowMap, 0); + for(int x = -1; x <= 1; x++) { + + for(int y = -1; y <= 1; y++) { + + float pcfDepth = texture(u_texture_directionalShadowMap, projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + + } + } - shadow /= 9.0; + shadow /= 9.0f; + return shadow; +} + +float computePointShadows(vec3 fragPos, vec3 lightPos) { + + // get vector between fragment position and light position + vec3 fragToLight = fragPos - lightPos; + + // use the light to fragment vector to sample from the depth map + float closestDepth = texture(u_texture_pointShadowMap0, fragToLight).r; + + // it is currently in linear range between [0,1]. Re-transform back to original value + closestDepth *= pointShadowDepthMapFarPlane; + + // 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; + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + return shadow; } diff --git a/res/shaders/basic.vert b/res/shaders/basic.vert index b4af7cd..706c677 100644 --- a/res/shaders/basic.vert +++ b/res/shaders/basic.vert @@ -8,18 +8,18 @@ out vec3 v_normal; out vec2 v_texCoord; out vec3 v_fragmentPosition; -out vec4 v_fragmentPositionLightSpace; +out vec4 v_fragmentPositionDirectionalLightSpace; uniform mat4 u_modelViewProjMatrix; uniform mat4 u_modelMatrix; -uniform mat4 u_lightViewProjMatrix; +uniform mat4 u_directionalLightViewProjMatrix; void main() { gl_Position = u_modelViewProjMatrix * vec4(a_position, 1.0f); v_fragmentPosition = vec3(u_modelMatrix * vec4(a_position, 1.0f)); - v_fragmentPositionLightSpace = u_lightViewProjMatrix * vec4(v_fragmentPosition, 1.0); + v_fragmentPositionDirectionalLightSpace = u_directionalLightViewProjMatrix * vec4(v_fragmentPosition, 1.0); v_normal = a_normal; v_texCoord = a_texCoord; diff --git a/res/shaders/shadowDepth.frag b/res/shaders/directionalShadowDepth.frag similarity index 97% rename from res/shaders/shadowDepth.frag rename to res/shaders/directionalShadowDepth.frag index 85f2da4..3ff54d5 100644 --- a/res/shaders/shadowDepth.frag +++ b/res/shaders/directionalShadowDepth.frag @@ -4,4 +4,4 @@ void main() { // Empty as we aren't rendering to any color buffer -} \ No newline at end of file +} diff --git a/res/shaders/shadowDepth.vert b/res/shaders/directionalShadowDepth.vert similarity index 98% rename from res/shaders/shadowDepth.vert rename to res/shaders/directionalShadowDepth.vert index dec698e..e7365fe 100644 --- a/res/shaders/shadowDepth.vert +++ b/res/shaders/directionalShadowDepth.vert @@ -8,4 +8,4 @@ void main() { gl_Position = u_modelViewProjMatrix * vec4(a_position, 1.0f); -} \ No newline at end of file +} diff --git a/res/shaders/pointShadowDepth.frag b/res/shaders/pointShadowDepth.frag new file mode 100644 index 0000000..100b98a --- /dev/null +++ b/res/shaders/pointShadowDepth.frag @@ -0,0 +1,17 @@ +#version 330 core +in vec4 v_fragmentPosition; + +uniform vec3 v_lightPos; +uniform float pointShadowDepthMapFarPlane; + +void main() { + + // Distance between fragment and light source + float lightDistance = length(v_fragmentPosition.xyz - v_lightPos); + + // map to [0;1] range + lightDistance = lightDistance / pointShadowDepthMapFarPlane; + + gl_FragDepth = lightDistance; + +} \ No newline at end of file diff --git a/res/shaders/pointShadowDepth.geom b/res/shaders/pointShadowDepth.geom new file mode 100644 index 0000000..b4eca83 --- /dev/null +++ b/res/shaders/pointShadowDepth.geom @@ -0,0 +1,25 @@ +#version 330 core +layout (triangles) in; +layout (triangle_strip, max_vertices=18) out; + +uniform mat4 u_shadowMatrices[6]; + +out vec4 v_fragmentPosition; + +void main() { + + for(int face = 0; face < 6; face++) { + + gl_Layer = face; + for(int i = 0; i < 3; i++) { + + v_fragmentPosition = gl_in[i].gl_Position; + gl_Position = u_shadowMatrices[face] * v_fragmentPosition; + EmitVertex(); + + } + + EndPrimitive(); + + } +} diff --git a/res/shaders/pointShadowDepth.vert b/res/shaders/pointShadowDepth.vert new file mode 100644 index 0000000..12021bb --- /dev/null +++ b/res/shaders/pointShadowDepth.vert @@ -0,0 +1,11 @@ +#version 330 core + +layout(location = 0) in vec3 a_position; + +uniform mat4 u_modelMatrix; + +void main() { + + gl_Position = u_modelMatrix * vec4(a_position, 1.0f); + +} diff --git a/res/shaders/postprocessing.frag b/res/shaders/postprocessing.frag index dcef925..82fbdb7 100644 --- a/res/shaders/postprocessing.frag +++ b/res/shaders/postprocessing.frag @@ -22,4 +22,4 @@ void main() { f_color = vec4(fragmentColor, 1.0f); -} \ No newline at end of file +} diff --git a/res/shaders/postprocessing.vert b/res/shaders/postprocessing.vert index ecd2527..ef49abd 100644 --- a/res/shaders/postprocessing.vert +++ b/res/shaders/postprocessing.vert @@ -8,4 +8,4 @@ void main() { v_tex_coords.x = (x+1.0)*0.5; v_tex_coords.y = (y+1.0)*0.5; gl_Position = vec4(x, y, 0, 1); -} \ No newline at end of file +} diff --git a/res/shaders/skybox.frag b/res/shaders/skybox.frag index e13255c..99c85c6 100644 --- a/res/shaders/skybox.frag +++ b/res/shaders/skybox.frag @@ -8,9 +8,8 @@ uniform samplerCube u_skybox; void main() { - vec3 fragmentColor = vec3(texture(u_skybox, v_texCoord)); f_color = vec4(fragmentColor, 1.0f); -} \ No newline at end of file +} diff --git a/res/shaders/skybox.vert b/res/shaders/skybox.vert index 3f98932..176ed4c 100644 --- a/res/shaders/skybox.vert +++ b/res/shaders/skybox.vert @@ -14,4 +14,4 @@ void main() { v_texCoord = a_position; -} \ No newline at end of file +} diff --git a/src/Controller.cpp b/src/Controller.cpp index e1ce563..5af3408 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -80,11 +80,12 @@ void Controller::run() { ShaderProgram lightProgram("res/shaders/light.vert", "res/shaders/light.frag"); ShaderProgram skyboxProgram("res/shaders/skybox.vert", "res/shaders/skybox.frag"); ShaderProgram postProcessingProgram("res/shaders/postprocessing.vert", "res/shaders/postprocessing.frag"); - ShaderProgram shadowDepthProgram("res/shaders/shadowDepth.vert", "res/shaders/shadowDepth.frag"); + ShaderProgram directionalShadowDepthProgram("res/shaders/directionalShadowDepth.vert", "res/shaders/directionalShadowDepth.frag"); + ShaderProgram pointShadowDepthProgram("res/shaders/pointShadowDepth.vert", "res/shaders/pointShadowDepth.geom", "res/shaders/pointShadowDepth.frag"); updateExposure(&postProcessingProgram); - Model model_backpack("res/models/backpack.ffo"); + //Model model_backpack("res/models/backpack.ffo"); //Model model_plant("res/models/plant.ffo"); //Model model_container("res/models/container.ffo"); Model model_cube("res/models/cube.ffo"); @@ -100,14 +101,16 @@ void Controller::run() { //Entity hut(&model_hut, &shaderProgram); //Entity moon(&model_moon, &shaderProgram); //Entity plant(&model_plant, &shaderProgram); - Entity dragon(&model_backpack, &shaderProgram); + Entity dragon(&model_dragon, &shaderProgram); Entity ground(&model_ground, &shaderProgram); Entity lightSource(&model_cube, &lightProgram); + dragon.setRotation(glm::vec3(0.0f)); dragon.setScale(0.2f); lightSource.setScale(0.1f); lightSource.setRotation(glm::vec3(0.f)); lightSource.setPosition(glm::vec3(-2.f, 1.5f, 2.f)); + lightSource.setIsLightSource(true); Skybox skybox(&model_cube, &skyboxProgram, "res/textures/skybox/"); @@ -127,45 +130,50 @@ void Controller::run() { // Update game // ... - static bool rotateLightSource = false; + static bool rotateLightSource = false, rotateEntity = false; if(rotateLightSource) { float radius = 4.0; glm::vec3 newPos = glm::vec3(-cos(glfwGetTime()*0.5), 0.5f, sin(glfwGetTime()*0.5)) * radius; world.getEntities()->operator[](1).setPosition(newPos); } + if(rotateEntity) { + world.getEntities()->operator[](0).rotate(glm::vec3(0.0f, 1.0f, 0.0f), 0.2f * deltaTime); + } static glm::vec3 lightColor = glm::vec3(1.f); - static float intensity = 20.0f; + static float intensity = 10.f; world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor * intensity); - world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.1f); + world.updateDirectionalLight(true, glm::vec3(-0.2f, -1.0f, -0.3f), lightColor * 0.25f); lightProgram.bind(); lightProgram.setUniform("v_lightColor", lightColor * 100.0f); lightProgram.unbind(); // Render and buffer swap glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Calc shadows + static bool drawShadows = true; + shaderProgram.bind(); + shaderProgram.setUniform("b_drawShadows", (int)drawShadows); + shaderProgram.unbind(); + if(drawShadows) + world.calculateShadows(&directionalShadowDepthProgram, &pointShadowDepthProgram); + pp_framebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - camera->lookForward(); camera->updateVPM(); - // Calc shadows - // ... - glViewport(0, 0, gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); + skybox.draw(camera->getView(), camera->getProj()); world.draw(camera->getViewProj(), camera->getPosition()); - static bool drawShadows = false; - if(drawShadows) - world.calculateShadows(&shadowDepthProgram); - pp_framebuffer->unbind(); pp_framebuffer->render(); #ifdef _DEBUG - renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateLightSource, &postProcessingProgram, &intensity, &drawShadows); + renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateEntity, &rotateLightSource, &postProcessingProgram, &intensity, &drawShadows); #endif glfwSwapBuffers(gameWindow->getGLFWwindow()); @@ -228,7 +236,7 @@ void Controller::updateExposure(ShaderProgram *shaderProgram) { } #ifdef _DEBUG -void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows) { +void Controller::renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateEntity, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -243,9 +251,10 @@ void Controller::renderImGui(std::vector *entites, PointLight *pointLigh ImGui::SliderFloat3("Position", translation, -4.0, 4.0); static float scale = 0.2f; ImGui::SliderFloat("Scale", &scale, 0.02, 2.0); + ImGui::Checkbox("Rotate Object", rotateEntity); entites->operator[](0).setPosition(glm::vec3(translation[0], translation[1], translation[2])); - entites->operator[](0).setRotation(glm::vec3(0.f,1.0f,0.f), rotation); + if(!*rotateEntity) entites->operator[](0).setRotation(glm::vec3(0.f,1.0f,0.f), rotation); entites->operator[](0).setScale(scale); // color picker diff --git a/src/Controller.h b/src/Controller.h index b5b5a3a..aab105b 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -29,7 +29,7 @@ private: void updateWindowSize(ShaderProgram *pp_program); void updateExposure(ShaderProgram *shaderProgram); - void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows); + void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateEntity, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows); Window *gameWindow; EventHandler *gameEventHandler; diff --git a/src/Entity.cpp b/src/Entity.cpp index 949f254..4f71a97 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -32,7 +32,7 @@ void Entity::draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition) { } -void Entity::drawShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram) { +void Entity::drawDirectionalShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram) { p_shaderProgram->bind(); @@ -46,6 +46,19 @@ void Entity::drawShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgra } +void Entity::drawPointShadows(ShaderProgram *p_shaderProgram) { + + p_shaderProgram->bind(); + + p_shaderProgram->setUniform("u_modelMatrix", modelMatrix); + + // Draw the model + model->drawWithoutTextures(); + + p_shaderProgram->unbind(); + +} + void Entity::translate(glm::vec3 vector) { position += vector; updateModelMatrix(); diff --git a/src/Entity.h b/src/Entity.h index cdb3b46..d621acc 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -15,7 +15,8 @@ public: ~Entity() = default; void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); - void drawShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram); + void drawDirectionalShadows(glm::mat4 viewProjMatrix, ShaderProgram *p_shaderProgram); + void drawPointShadows(ShaderProgram *p_shaderProgram); void translate(glm::vec3 vector); void rotate(glm::vec3 axis, float radians); @@ -25,17 +26,21 @@ public: void setRotation(glm::vec3 axis, float radians); void setScale(float scaleFactor); + void setIsLightSource(bool temp) { isLightSource = temp;} + void setId(uint32_t id) { this->id = id; } uint32_t getId() { return id; } glm::vec3 getPosition() { return position; } glm::mat4 getModelMatrix() { return modelMatrix; } + bool getIsLightSource() { return isLightSource; } private: void updateModelMatrix(); uint32_t id; + bool isLightSource = false; glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); glm::quat quaternion; diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index 1b91a38..b716e71 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -68,25 +68,42 @@ void Framebuffer::render() { -DepthMap::DepthMap(int RESOLUTION) { +DepthMap::DepthMap(int TYPE, int RESOLUTION) + : cubeMap(RESOLUTION) { - glGenFramebuffers(1, &depthMapFBO); + if(TYPE == DEPTHMAP_NORMAL) { - glGenTextures(1, &depthMap); - glBindTexture(GL_TEXTURE_2D, depthMap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, RESOLUTION, RESOLUTION, 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_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenFramebuffers(1, &depthMapFBO); - glBindTexture(GL_TEXTURE_2D, 0); + glGenTextures(1, &depthMap); + glBindTexture(GL_TEXTURE_2D, depthMap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, RESOLUTION, RESOLUTION, 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_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - bind(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - unbind(); + glBindTexture(GL_TEXTURE_2D, 0); + + bind(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + unbind(); + + } else if(TYPE == DEPTHMAP_CUBEMAP) { + + glGenFramebuffers(1, &depthMapFBO); + + // CubeMap is already created + + bind(); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cubeMap.getTextureId(), 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + unbind(); + + } } diff --git a/src/Framebuffer.h b/src/Framebuffer.h index 56e6098..4c80dfc 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -2,6 +2,7 @@ #include #include "ShaderProgram.h" +#include "Texture.h" class Framebuffer { @@ -28,21 +29,31 @@ private: }; +enum depthMapType {DEPTHMAP_NORMAL, DEPTHMAP_CUBEMAP}; +// Framebuffer without color buffer. (Shadows) class DepthMap { public: - DepthMap(int RESOLUTION); + // Normal depthMap with texture and point depthMap with cubeMap + DepthMap(int TYPE, int RESOLUTION); void bind(); void unbind(); + GLuint getFBO() { return depthMapFBO; } GLuint getDepthMap() { return depthMap; } + GLuint getCubeMapId() { return cubeMap.getTextureId(); } private: GLuint depthMapFBO; + + // Either a normal depthMap is used (Directional shadows) + // or a cubeMap is used (Point shadows) GLuint depthMap; + CubeMap cubeMap; + }; \ No newline at end of file diff --git a/src/Light.h b/src/Light.h index 4268a89..4a82df3 100644 --- a/src/Light.h +++ b/src/Light.h @@ -33,7 +33,6 @@ public: protected: - Light() = default; Light(ShaderProgram *shaderProgram) : shaderProgram(shaderProgram) {} ShaderProgram *shaderProgram; @@ -53,7 +52,6 @@ class PointLight : public Light { public: - PointLight() = default; PointLight(ShaderProgram *shaderProgram); void setPosition(glm::vec3 position) { @@ -65,6 +63,8 @@ public: void setId(unsigned int id) { lightId = id; } + glm::vec3 getPosition() { return position; } + private: @@ -82,7 +82,6 @@ class DirectionalLight : public Light { public: - DirectionalLight() = default; DirectionalLight(ShaderProgram *shaderProgram); void setDirection(glm::vec3 direction) { diff --git a/src/ShaderProgram.cpp b/src/ShaderProgram.cpp index 1f53bfb..6305a00 100644 --- a/src/ShaderProgram.cpp +++ b/src/ShaderProgram.cpp @@ -6,13 +6,64 @@ #include "ShaderProgram.h" ShaderProgram::ShaderProgram(const char* vertexShaderPath, const char* fragmentShaderPath) { - shaderProgramId = createShader(vertexShaderPath, fragmentShaderPath); - // Set transformation matrix as default to identity matrix - bind(); - glm::mat4 identity_matrix = glm::mat4(1.0f); - setUniform("transform", identity_matrix); - unbind(); + std::string vertexShaderSource = parse(vertexShaderPath); + std::string fragmentShaderSource = parse(fragmentShaderPath); + + shaderProgramId = glCreateProgram(); + GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); + GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER); + + glAttachShader(shaderProgramId, vs); + glAttachShader(shaderProgramId, fs); + + glLinkProgram(shaderProgramId); + + GLint isLinked = 0; + glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &isLinked); + if(!isLinked) std::cout << "Failed to link shaderProgram: " << vertexShaderPath << ", " << fragmentShaderPath << std::endl; + + #ifdef _RELEASE + glDetachShader(program, vs); + glDetachShader(program, fs); + + glDeleteShader(vs); + glDeleteShader(fs); + #endif + +} + +ShaderProgram::ShaderProgram(const char* vertexShaderPath, const char* geometryShaderPath, const char* fragmentShaderPath) { + + std::string vertexShaderSource = parse(vertexShaderPath); + std::string geometryShaderSource = parse(geometryShaderPath); + std::string fragmentShaderSource = parse(fragmentShaderPath); + + shaderProgramId = glCreateProgram(); + GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); + GLuint gs = compile(geometryShaderSource, GL_GEOMETRY_SHADER); + GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER); + + glAttachShader(shaderProgramId, vs); + glAttachShader(shaderProgramId, gs); + glAttachShader(shaderProgramId, fs); + + glLinkProgram(shaderProgramId); + + GLint isLinked = 0; + glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &isLinked); + if(!isLinked) std::cout << "Failed to link shaderProgram: " << vertexShaderPath << ", " << geometryShaderPath << ", " << fragmentShaderPath << std::endl; + + #ifdef _RELEASE + glDetachShader(program, vs); + glDetachShader(program, gs); + glDetachShader(program, fs); + + glDeleteShader(vs); + glDeleteShader(gs); + glDeleteShader(fs); + #endif + } ShaderProgram::~ShaderProgram() { @@ -27,35 +78,11 @@ void ShaderProgram::unbind() { glUseProgram(0); } -GLuint ShaderProgram::createShader(const char* vertexShaderPath, const char* framentShaderPath) { - std::string vertexShaderSource = parse(vertexShaderPath); - std::string fragmentShaderSource = parse(framentShaderPath); - - GLuint program = glCreateProgram(); - GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); - GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER); - - glAttachShader(program, vs); - glAttachShader(program, fs); - - glLinkProgram(program); - - #ifdef _RELEASE - glDetachShader(program, vs); - glDetachShader(program, fs); - - glDeleteShader(vs); - glDeleteShader(fs); - #endif - - return program; -} - std::string ShaderProgram::parse(const char* filename) { FILE* file; file = fopen(filename, "rb"); if(!file) { - std::cout << "File " << filename << " not found!" << std::endl; + std::cout << "Shader " << filename << " not found!" << std::endl; exit(-1); } diff --git a/src/ShaderProgram.h b/src/ShaderProgram.h index 8062570..5f6c961 100644 --- a/src/ShaderProgram.h +++ b/src/ShaderProgram.h @@ -8,7 +8,8 @@ class ShaderProgram { public: - ShaderProgram(const char* vertexShaderPath, const char* framentShaderPath); + ShaderProgram(const char* vertexShaderPath, const char* fragmentShaderPath); + ShaderProgram(const char* vertexShaderPath, const char* geometryShaderPath, const char* fragmentShaderPath); ~ShaderProgram(); void bind(); @@ -28,7 +29,6 @@ private: std::string parse(const char* filename); GLuint compile(std::string shaderSource, GLenum type); - GLuint createShader(const char* vertexShaderPath, const char* framentShaderPath); GLuint shaderProgramId; diff --git a/src/Texture.cpp b/src/Texture.cpp index 7b762e0..52a612f 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -94,7 +94,7 @@ CubeMap::CubeMap(const char* texturePseudoPath) { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 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); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); for(unsigned int i = 0; i < CUBEMAP_FACES_NUM_ITEMS; i++) { @@ -115,6 +115,26 @@ CubeMap::CubeMap(const char* texturePseudoPath) { } +CubeMap::CubeMap(int RESOLUTION) + : textureWidth(RESOLUTION), textureHeight(RESOLUTION) { + + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_CUBE_MAP, textureId); + + 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); + + const int CUBEMAP_NUM_FACES = 6; + for (unsigned int i = 0; i < CUBEMAP_NUM_FACES; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, RESOLUTION, RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + +} + CubeMap::~CubeMap() { glDeleteTextures(1, &textureId); } diff --git a/src/Texture.h b/src/Texture.h index 6e78f55..93b49ea 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -44,11 +44,15 @@ class CubeMap { public: CubeMap(const char* texturePseudoPath); + CubeMap(int RESOLUTION); + ~CubeMap(); void bind(ShaderProgram *shaderProgram); void unbind(); + GLuint getTextureId() { return textureId; } + private: void fillTexturePathVector(const char* texturePseudoPath); diff --git a/src/World.cpp b/src/World.cpp index af0697f..ed1148a 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -2,21 +2,23 @@ #include -// At this moment, I don't have any idea how to implement the initializer list -// with an array of objects that don't have an default contructor. So the default -// constructors for Light and PointLight have only this reason to exist. - World::World(ShaderProgram *shaderProgram) - : shaderProgram(shaderProgram), depthMapFBO(SHADOW_RES) { + : shaderProgram(shaderProgram), + directionalLight(shaderProgram), + depthMapDirectionalFBO(DEPTHMAP_NORMAL, SHADOW_RES) { // PointLights for(unsigned int i = 0; i < NUM_POINT_LIGHTS; i++) { - pointLights[i].setId(i); - pointLights[i].setShaderProgram(shaderProgram); + PointLight new_pointLight(shaderProgram); + new_pointLight.setId(i); + pointLights.push_back(new_pointLight); } - // DirectionalLight - directionalLight.setShaderProgram(shaderProgram); + // Create 4 depthMaps + for(int i = 0; i < 4; i++) { + DepthMap *temp_depthMap = new DepthMap(DEPTHMAP_CUBEMAP, SHADOW_RES); + depthMapPointFBO.push_back(temp_depthMap); + } // This will be removed in future when gloss maps are implemented shaderProgram->bind(); @@ -25,6 +27,15 @@ World::World(ShaderProgram *shaderProgram) } +World::~World() { + + // Iterate over depthMapPointFBO vector and delete all items + for(auto it = depthMapPointFBO.begin(); it != depthMapPointFBO.end(); it++) { + delete (*it); + } + +} + void World::addEntity(Entity entity) { uint32_t new_id = entities.size(); entity.setId(new_id); @@ -67,50 +78,96 @@ void World::draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition) { } -void World::calculateShadows(ShaderProgram *p_shaderProgram) { +void World::calculateShadows(ShaderProgram *directionalShaderProgram, ShaderProgram *pointShaderProgram) { // Get old viewport dimensions to reset them later... GLint VIEWPORT[4]; glGetIntegerv(GL_VIEWPORT, VIEWPORT); glViewport(0, 0, SHADOW_RES, SHADOW_RES); - - depthMapFBO.bind(); - - glClear(GL_DEPTH_BUFFER_BIT); - // ConfigureShaderAndMatrices(); - float near_plane = 1.0f, far_plane = 15.0f; - glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - glm::mat4 lightView = glm::lookAt(-5.0f * glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); - glm::mat4 lightViewProjectionMatrix = lightProjection * lightView; - // Switch face culling (Peter panning) glCullFace(GL_BACK); + depthMapDirectionalFBO.bind(); + + glClear(GL_DEPTH_BUFFER_BIT); + + // --- Directional shadows --- + glm::mat4 directionalLightView = glm::lookAt(-5.0f * glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); + glm::mat4 directionalLightViewProjectionMatrix = directionalLightProjection * directionalLightView; + // Draw scene from light perspective // Draw all entities for(auto it = entities.begin(); it != entities.end(); it++) { - it->drawShadows(lightViewProjectionMatrix, p_shaderProgram); + it->drawDirectionalShadows(directionalLightViewProjectionMatrix, directionalShaderProgram); } - glCullFace(GL_FRONT); - - depthMapFBO.unbind(); - - // Reset viewport size - glViewport(VIEWPORT[0], VIEWPORT[1], VIEWPORT[2], VIEWPORT[3]); + depthMapDirectionalFBO.unbind(); shaderProgram->bind(); // Send lightViewProjMatrix to basic shader - shaderProgram->setUniform("u_lightViewProjMatrix", lightViewProjectionMatrix); + shaderProgram->setUniform("u_directionalLightViewProjMatrix", directionalLightViewProjectionMatrix); // Send shadowMap to basic shader - const int textureUnit = TEXTURE_TYPE_NUM_ITEMS * 2; - shaderProgram->setUniform("u_texture_shadowMap", (int)textureUnit); + int textureUnit = TEXTURE_TYPE_NUM_ITEMS * 2; + shaderProgram->setUniform("u_texture_directionalShadowMap", (int)textureUnit); glActiveTexture(GL_TEXTURE0 + textureUnit); - glBindTexture(GL_TEXTURE_2D, depthMapFBO.getDepthMap()); + glBindTexture(GL_TEXTURE_2D, depthMapDirectionalFBO.getDepthMap()); shaderProgram->unbind(); + + // --- Point shadows --- + + // 4 depthMaps for 4 point lights + for(int i = 0; i < 1; i++) { + + depthMapPointFBO[i]->bind(); + + glClear(GL_DEPTH_BUFFER_BIT); + + // Create 6 view matrices for every face of the cubeMap + std::vector viewProjMatrices; + glm::vec3 lightPos = pointLights[i].getPosition(); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f))); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f))); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + viewProjMatrices.push_back(pointLightProjection * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))); + + pointShaderProgram->bind(); + + for(int i = 0; i < 6; i++) { + pointShaderProgram->setUniform(("u_shadowMatrices[" + std::to_string(i) + "]").c_str(), viewProjMatrices[i]); + } + + pointShaderProgram->setUniform("pointShadowDepthMapFarPlane", far); + pointShaderProgram->setUniform("v_lightPos", lightPos); + + // Draw scene from light perspective + // Draw all entities + for(auto it = entities.begin(); it != entities.end(); it++) { + it->drawPointShadows(pointShaderProgram); + } + + depthMapPointFBO[i]->unbind(); + + shaderProgram->bind(); + + shaderProgram->setUniform("pointShadowDepthMapFarPlane", far); + + textureUnit = TEXTURE_TYPE_NUM_ITEMS * 2 + i + 1; + shaderProgram->setUniform("u_texture_pointShadowMap0", (int)textureUnit); + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_CUBE_MAP, depthMapPointFBO[i]->getCubeMapId()); + + shaderProgram->unbind(); + } + + + // Reset viewport size + glViewport(VIEWPORT[0], VIEWPORT[1], VIEWPORT[2], VIEWPORT[3]); + glCullFace(GL_FRONT); } diff --git a/src/World.h b/src/World.h index 8f89b8b..771acac 100644 --- a/src/World.h +++ b/src/World.h @@ -13,7 +13,7 @@ class World { public: World(ShaderProgram *shaderProgram); - ~World() = default; + ~World(); void addEntity(Entity entity); void removeEntity(uint32_t id); @@ -23,10 +23,10 @@ public: std::vector * getEntities() { return &entities; } - PointLight * getPointLights() { return pointLights; } + PointLight * getPointLights() { return pointLights.data(); } void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); - void calculateShadows(ShaderProgram *shaderProgram); + void calculateShadows(ShaderProgram *directionalShaderProgram, ShaderProgram *pointShaderProgram); private: @@ -36,11 +36,17 @@ private: // Lights DirectionalLight directionalLight; - PointLight pointLights[NUM_POINT_LIGHTS]; + std::vector pointLights; //SpotLight spotLight; // Shadows - const int SHADOW_RES = 4096; - DepthMap depthMapFBO; + const int SHADOW_RES = 4096/4; + DepthMap depthMapDirectionalFBO; + std::vector depthMapPointFBO; + // Shadow projection matrices + float near_plane = 1.0f, far_plane = 15.0f; + glm::mat4 directionalLightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + float aspect = 1.0f, near = 1.0f, far = 25.0f; + glm::mat4 pointLightProjection = glm::perspective(glm::radians(90.0f), aspect, near, far); }; From 911e004291402a2b50430a21b27776b3a0e4bd0c Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Sat, 10 Oct 2020 19:51:12 +0200 Subject: [PATCH 8/8] PCF for point shadows --- .gitignore | 2 ++ res/shaders/basic.frag | 33 ++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index cd2265b..0b04984 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ build lib/assimp/libassimp.so.5.0.0 res/models res/textures +.kdev4/Fall-Fever.kdev4 +Fall-Fever.kdev4 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index b982362..d5f83c1 100644 --- a/res/shaders/basic.frag +++ b/res/shaders/basic.frag @@ -70,6 +70,14 @@ uniform samplerCube u_texture_pointShadowMap0; //uniform samplerCube u_texture_pointShadowMap2; //uniform samplerCube u_texture_pointShadowMap3; +vec3 sampleOffsetDirections[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) +); + uniform bool b_drawShadows; uniform float pointShadowDepthMapFarPlane; @@ -120,8 +128,7 @@ vec3 directionalLightContribution(DirectionalLight light, vec3 normal, vec3 view computeShading(light.ambient, light.diffuse, light.specular, lightDir, viewDir, normal, ambient, diffuse, specular); float shadows = 0.0f; - //if(b_drawShadows) - if(false) + if(b_drawShadows) shadows = computeDirectionalShadows(v_fragmentPositionDirectionalLightSpace, normal, lightDir); return (ambient + (1.0f - shadows) * (diffuse + specular)); @@ -245,18 +252,22 @@ float computePointShadows(vec3 fragPos, vec3 lightPos) { // get vector between fragment position and light position vec3 fragToLight = fragPos - lightPos; - // use the light to fragment vector to sample from the depth map - float closestDepth = texture(u_texture_pointShadowMap0, fragToLight).r; - - // it is currently in linear range between [0,1]. Re-transform back to original value - closestDepth *= pointShadowDepthMapFarPlane; - // 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; - float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + float shadow = 0.0; + float bias = 0.05; + int samples = 20; + float viewDistance = length(u_viewPosition - fragPos); + float diskRadius = 0.05; + for(int i = 0; i < samples; ++i) { + float closestDepth = texture(u_texture_pointShadowMap0, fragToLight + sampleOffsetDirections[i] * diskRadius).r; + closestDepth *= pointShadowDepthMapFarPlane; + if(currentDepth - bias > closestDepth) + shadow += 1.0; + } + + shadow /= float(samples); return shadow; }