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] 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); };