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/imgui.ini b/imgui.ini index ebf8c46..d3c73fe 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=12,15 +Size=871,365 Collapsed=0 diff --git a/res/shaders/basic.frag b/res/shaders/basic.frag index 9919065..d5f83c1 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_fragmentPositionDirectionalLightSpace; struct Material { sampler2D texture_diffuse0; @@ -35,8 +36,6 @@ struct PointLight { bool isActive; vec3 position; - float K_c; - float K_l; float K_q; vec3 ambient; @@ -54,8 +53,6 @@ struct SpotLight { float innerCutOff; float outerCutOff; - float K_c; - float K_l; float K_q; vec3 ambient; @@ -67,6 +64,24 @@ uniform SpotLight u_spotLight; uniform mat3 u_normalMatrix; uniform vec3 u_viewPosition; +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; + +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; + 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); @@ -77,7 +92,10 @@ 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); + +float computeDirectionalShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir); +float computePointShadows(vec3 fragPos, vec3 lightPos); void main() { @@ -109,7 +127,11 @@ 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; + float shadows = 0.0f; + if(b_drawShadows) + shadows = computeDirectionalShadows(v_fragmentPositionDirectionalLightSpace, normal, lightDir); + + return (ambient + (1.0f - shadows) * (diffuse + specular)); } vec3 pointLightContribution(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { @@ -123,12 +145,16 @@ 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; - 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) { @@ -142,7 +168,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,9 +204,70 @@ 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); } + +float computeDirectionalShadows(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) { + + // Perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + // Transform from [-1,1] to [0,1] + projCoords *= 0.5f; + projCoords += 0.5f; + + if(projCoords.z > 1.0f) return 0.0f; + + 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_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.0f; + + + return shadow; +} + +float computePointShadows(vec3 fragPos, vec3 lightPos) { + + // get vector between fragment position and light position + vec3 fragToLight = fragPos - lightPos; + + // now get current linear depth as the length between the fragment and light position + float currentDepth = length(fragToLight); + + 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; +} diff --git a/res/shaders/basic.vert b/res/shaders/basic.vert index 4f13209..706c677 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_fragmentPositionDirectionalLightSpace; uniform mat4 u_modelViewProjMatrix; uniform mat4 u_modelMatrix; +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_fragmentPositionDirectionalLightSpace = u_directionalLightViewProjMatrix * vec4(v_fragmentPosition, 1.0); v_normal = a_normal; v_texCoord = a_texCoord; diff --git a/res/shaders/directionalShadowDepth.frag b/res/shaders/directionalShadowDepth.frag new file mode 100644 index 0000000..3ff54d5 --- /dev/null +++ b/res/shaders/directionalShadowDepth.frag @@ -0,0 +1,7 @@ +#version 330 core + +void main() { + + // Empty as we aren't rendering to any color buffer + +} diff --git a/res/shaders/directionalShadowDepth.vert b/res/shaders/directionalShadowDepth.vert new file mode 100644 index 0000000..e7365fe --- /dev/null +++ b/res/shaders/directionalShadowDepth.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); + +} 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 new file mode 100644 index 0000000..82fbdb7 --- /dev/null +++ b/res/shaders/postprocessing.frag @@ -0,0 +1,25 @@ +#version 330 core + +const float GAMMA = 2.2f; + +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)); + + f_color = vec4(fragmentColor, 1.0f); + +} diff --git a/res/shaders/postprocessing.vert b/res/shaders/postprocessing.vert new file mode 100644 index 0000000..ef49abd --- /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); +} diff --git a/res/shaders/skybox.frag b/res/shaders/skybox.frag index ba0bbdd..99c85c6 100644 --- a/res/shaders/skybox.frag +++ b/res/shaders/skybox.frag @@ -8,6 +8,8 @@ uniform samplerCube u_skybox; void main() { - f_color = texture(u_skybox, v_texCoord); + vec3 fragmentColor = vec3(texture(u_skybox, v_texCoord)); -} \ No newline at end of file + f_color = vec4(fragmentColor, 1.0f); + +} 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/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/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 6fff68e..5af3408 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,29 @@ 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"); + 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_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"); @@ -100,10 +105,12 @@ void Controller::run() { 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/"); @@ -114,6 +121,8 @@ void Controller::run() { camera->translate(glm::vec3(0.0f, 1.5f, 5.0f)); + pp_framebuffer = new Framebuffer(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, &postProcessingProgram); + // This is the game loop while(!glfwWindowShouldClose(gameWindow->getGLFWwindow())) { // Timing @@ -121,38 +130,57 @@ void Controller::run() { // Update game // ... - static bool rotateLightSource = 0; + 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); - world.updatePointLight(0, true, world.getEntities()->operator[](1).getPosition(), lightColor); - world.updateDirectionalLight(true, glm::vec3(1.0f), lightColor); + 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.25f); lightProgram.bind(); - lightProgram.setUniform("v_lightColor", lightColor); + 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(); - skybox.draw(camera->getView(), camera->getProj()); + glViewport(0, 0, gameWindow->getWindowWidth(), gameWindow->getWindowHeight()); + skybox.draw(camera->getView(), camera->getProj()); world.draw(camera->getViewProj(), camera->getPosition()); + pp_framebuffer->unbind(); + pp_framebuffer->render(); + #ifdef _DEBUG - renderImGui(world.getEntities(), &lightColor, &rotateLightSource); + renderImGui(world.getEntities(), &world.getPointLights()[0], &lightColor, &rotateEntity, &rotateLightSource, &postProcessingProgram, &intensity, &drawShadows); #endif glfwSwapBuffers(gameWindow->getGLFWwindow()); // Update window size if(gameWindow->checkWindowWasResized()) - updateWindowSize(); + updateWindowSize(&postProcessingProgram); // Check events, handle input gameEventHandler->handleEvents(); @@ -193,13 +221,22 @@ 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_program); +} + +void Controller::updateExposure(ShaderProgram *shaderProgram) { + shaderProgram->bind(); + shaderProgram->setUniform("u_exposure", exposure); + shaderProgram->unbind(); } #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 *rotateEntity, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -207,26 +244,40 @@ void Controller::renderImGui(std::vector *entites, glm::vec3 *lightColor // 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}; - 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); + ImGui::Checkbox("Rotate Object", rotateEntity); - entites->operator[](0).setPosition(glm::vec3(translation[0], 0.0f, translation[1])); - entites->operator[](0).setRotation(glm::vec3(0.f,1.0f,0.f), rotation); + entites->operator[](0).setPosition(glm::vec3(translation[0], translation[1], translation[2])); + if(!*rotateEntity) 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"); + static float K_q = 1.0f; + ImGui::SliderFloat("Attenuation Parameter", &K_q, 0, 1.5f); + + updateExposure(postProcessingProgram); + pointLight->setParameters(K_q); + static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + 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("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 dfcf5e3..aab105b 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 { @@ -24,17 +26,22 @@ private: void limit_framerate(); - void updateWindowSize(); + void updateWindowSize(ShaderProgram *pp_program); + void updateExposure(ShaderProgram *shaderProgram); - void renderImGui(std::vector *entites, glm::vec3 *lightColor, bool *rotateLightSource); + void renderImGui(std::vector *entites, PointLight *pointLight, glm::vec3 *lightColor, bool *rotateEntity, bool *rotateLightSource, ShaderProgram *postProcessingProgram, float *intensity, bool *drawShadows); Window *gameWindow; EventHandler *gameEventHandler; Camera *camera; + Framebuffer *pp_framebuffer; + const uint16_t MAX_FPS = 60; double deltaTime; bool wireFrameMode = 0; + float exposure = 1.0f; + }; diff --git a/src/Entity.cpp b/src/Entity.cpp index 314f214..0dea7d7 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -32,6 +32,33 @@ void Entity::draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition) { } +void Entity::drawDirectionalShadows(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::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 635789e..d621acc 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -15,6 +15,8 @@ public: ~Entity() = default; void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); + 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); @@ -24,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 new file mode 100644 index 0000000..b716e71 --- /dev/null +++ b/src/Framebuffer.cpp @@ -0,0 +1,116 @@ +#include "Framebuffer.h" + +#include +#include + +Framebuffer::Framebuffer(uint32_t width, uint32_t height, ShaderProgram *shaderProgram) { + + this->shaderProgram = shaderProgram; + + 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); +} + +void Framebuffer::render() { + + // 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 TYPE, int RESOLUTION) + : cubeMap(RESOLUTION) { + + if(TYPE == DEPTHMAP_NORMAL) { + + glGenFramebuffers(1, &depthMapFBO); + + 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); + + 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(); + + } + +} + +void DepthMap::bind() { + glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); +} + +void DepthMap::unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} diff --git a/src/Framebuffer.h b/src/Framebuffer.h new file mode 100644 index 0000000..4c80dfc --- /dev/null +++ b/src/Framebuffer.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include "ShaderProgram.h" +#include "Texture.h" + +class Framebuffer { + +public: + + Framebuffer(uint32_t width, uint32_t height, ShaderProgram *ShaderProgram); + ~Framebuffer(); + + void bind(); + void unbind(); + + void render(); + + GLuint getTextureId() { + return textures[0]; + } + +private: + + GLuint FBO; + GLuint textures[2]; + + ShaderProgram *shaderProgram; + +}; + +enum depthMapType {DEPTHMAP_NORMAL, DEPTHMAP_CUBEMAP}; + +// Framebuffer without color buffer. (Shadows) +class DepthMap { + +public: + + // 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.cpp b/src/Light.cpp index 8c745ef..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(); @@ -45,9 +43,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..4a82df3 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,14 +29,16 @@ public: update(); } + glm::vec3 getColor() { return lightColor; } + protected: - Light() = default; Light(ShaderProgram *shaderProgram) : shaderProgram(shaderProgram) {} ShaderProgram *shaderProgram; bool isActive = false; + bool shouldCastShadow = true; // Color glm::vec3 lightColor; @@ -50,7 +52,6 @@ class PointLight : public Light { public: - PointLight() = default; PointLight(ShaderProgram *shaderProgram); void setPosition(glm::vec3 position) { @@ -58,8 +59,12 @@ public: update(); } + void setParameters(float K_q) { this->K_q = K_q; } + void setId(unsigned int id) { lightId = id; } + glm::vec3 getPosition() { return position; } + private: @@ -68,8 +73,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; }; @@ -78,7 +82,6 @@ class DirectionalLight : public Light { public: - DirectionalLight() = default; DirectionalLight(ShaderProgram *shaderProgram); void setDirection(glm::vec3 direction) { @@ -90,8 +93,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/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/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 13624da..52a612f 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); @@ -91,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++) { @@ -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); @@ -112,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/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.cpp b/src/World.cpp index edf570e..ed1148a 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -2,29 +2,40 @@ #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) { + : 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(); - shaderProgram->setUniform("u_material.shininess", 64.0f); + shaderProgram->setUniform("u_material.shininess", 100.0f); shaderProgram->unbind(); } +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); @@ -57,8 +68,106 @@ 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 *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); + // 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->drawDirectionalShadows(directionalLightViewProjectionMatrix, directionalShaderProgram); + } + + depthMapDirectionalFBO.unbind(); + + shaderProgram->bind(); + + // Send lightViewProjMatrix to basic shader + shaderProgram->setUniform("u_directionalLightViewProjMatrix", directionalLightViewProjectionMatrix); + + // Send shadowMap to basic shader + int textureUnit = TEXTURE_TYPE_NUM_ITEMS * 2; + shaderProgram->setUniform("u_texture_directionalShadowMap", (int)textureUnit); + glActiveTexture(GL_TEXTURE0 + textureUnit); + 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 811365f..771acac 100644 --- a/src/World.h +++ b/src/World.h @@ -5,13 +5,15 @@ #include "Light.h" #include "Camera.h" #include "Entity.h" +#include "ShaderProgram.h" +#include "Framebuffer.h" class World { public: World(ShaderProgram *shaderProgram); - ~World() = default; + ~World(); void addEntity(Entity entity); void removeEntity(uint32_t id); @@ -21,7 +23,10 @@ public: std::vector * getEntities() { return &entities; } + PointLight * getPointLights() { return pointLights.data(); } + void draw(glm::mat4 viewProjMatrix, glm::vec3 viewPosition); + void calculateShadows(ShaderProgram *directionalShaderProgram, ShaderProgram *pointShaderProgram); private: @@ -29,8 +34,19 @@ private: std::vector entities; + // Lights DirectionalLight directionalLight; - PointLight pointLights[NUM_POINT_LIGHTS]; + std::vector pointLights; //SpotLight spotLight; + // Shadows + 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); + };