#include "light.h" #include "framebuffer.h" #include "texture.h" #include "scene.h" #include "shader.h" #include "shadersource.h" #include "phongmaterial.h" #include "mesh.h" #include "camera.h" #include #include RESOURCE_PACK(shaders) const char* Light::flagStr[] = { "AMBIENT_LIGHT", "DIRECTIONNAL_LIGHT", "POINT_LIGHT", "SPOT_LIGHT", "SHADOWMAP", "CASCADED" }; const glm::mat4 Light::biasMatrix( 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0); void AmbientLight::bindAttributes(Shader *shader, Camera *camera) { shader->bindVec3(shader->getLocation("lightColor"), m_color); } // DIRECTIONNAL LIGHT int DirectionnalLight::m_shaderRefCounter = 0; Shader* DirectionnalLight::m_shaders[4] = {NULL}; DirectionnalLight::DirectionnalLight(glm::vec3 dir, glm::vec3 lightColor) : m_direction(dir), m_shadowCaster(false) { m_color = lightColor; m_shadowMap = NULL; } void DirectionnalLight::bindAttributes(Shader *shader, Camera *camera) { shader->bindVec3(shader->getLocation("lightColor"), m_color); glm::vec4 direction = glm::vec4(m_direction, 0.f); shader->bindVec3(shader->getLocation("dirLight"), glm::normalize(glm::vec3(camera->getViewMatrix()*direction))); if(m_shadowCaster) { switch(m_projectionMatrices.size()) { case 1: { m_shadowMap->getTexture(0)->bind(7); // TODO use something else than 7 shader->bindInteger(shader->getLocation("shadowMap"), 7); glm::mat4 viewToLightMatrix = Light::biasMatrix * m_projectionMatrices[0] * m_viewMatrices[0] * glm::inverse(camera->getViewMatrix()); shader->bindMat4(shader->getLocation("viewToLightMatrix"), viewToLightMatrix); } break; case 3: { m_shadowMap->getTexture(0)->bind(7); // TODO use something else than 7 shader->bindInteger(shader->getLocation("shadowMap"), 7); glm::mat4 lightMVP; lightMVP = Light::biasMatrix * m_projectionMatrices[0] * (m_viewMatrices[0] * glm::inverse(camera->getViewMatrix())); shader->bindMat4(shader->getLocation("frontShadowMVP"), lightMVP); lightMVP = Light::biasMatrix * m_projectionMatrices[1] * (m_viewMatrices[1] * glm::inverse(camera->getViewMatrix())); shader->bindMat4(shader->getLocation("midShadowMVP"), lightMVP); lightMVP = Light::biasMatrix * m_projectionMatrices[2] * (m_viewMatrices[2] * glm::inverse(camera->getViewMatrix())); shader->bindMat4(shader->getLocation("backShadowMVP"), lightMVP); } break; default: break; } } } unsigned int DirectionnalLight::getFlags() { unsigned int flag = 1 << DIRECTIONNAL_FLAG; if(m_shadowCaster) flag |= 1 << SHADOWMAP_FLAG; if(m_projectionMatrices.size() == 3) flag |= 1 << CASCADED_FLAG; return flag; } void DirectionnalLight::initShadowMap(int resolution, bool isCascaded) { m_shadowMapResolution = resolution; // shader compilation if(m_shaderRefCounter == 0) { ShaderSource source; Resource::ResourceMap shaderMap; Resource::getResourcePack_shaders(shaderMap); source.setSource(shaderMap["shaders/shadow.vert.glsl"], ShaderSource::VERTEX); source.setSource(shaderMap["shaders/shadow.frag.glsl"], ShaderSource::FRAGMENT); unsigned int flag = 1 << DIRECTIONNAL_FLAG; m_shaders[0] = source.compile(Mesh::MESH_3D, flag); m_shaders[1] = source.compile(Mesh::MESH_3D & Mesh::MATERIAL_ALPHA_MASK, flag); source.setSource(shaderMap["shaders/shadow.geom.glsl"], ShaderSource::GEOMETRY); flag |= 1 << CASCADED_FLAG; m_shaders[2] = source.compile(Mesh::MESH_3D, flag); m_shaders[3] = source.compile(Mesh::MESH_3D & Mesh::MATERIAL_ALPHA_MASK, flag); } if(!m_shadowCaster) ++m_shaderRefCounter; m_shadowCaster = true; if(isCascaded) { m_viewMatrices.resize(3); m_projectionMatrices.resize(3); } else { m_viewMatrices.resize(1); m_projectionMatrices.resize(1); } // Depth buffer Texture *tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, resolution, resolution, GL_FLOAT, isCascaded ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); if(isCascaded) { tex->bind(); for(int i=0; i<3; ++i) glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT, resolution, resolution, i, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); } tex->setFiltering(GL_LINEAR); tex->setWrap(GL_CLAMP_TO_EDGE); tex->setParameter(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); tex->setParameter(GL_TEXTURE_COMPARE_FUNC, GL_LESS); // framebuffer m_shadowCaster = true; m_shadowMap = new FrameBuffer(); m_shadowMap->addTexture(tex, GL_DEPTH_ATTACHMENT); m_shadowMap->initColorAttachments(); } void DirectionnalLight::destroyShadowMap() { if(m_shadowCaster) { m_shadowCaster = false; --m_shaderRefCounter; if(m_shaderRefCounter == 0) { for(int i=0; i<4; ++i) delete m_shaders[i]; } if(m_shadowMap != NULL) delete m_shadowMap; } m_viewMatrices.clear(); m_projectionMatrices.clear(); m_shadowMap = NULL; } void DirectionnalLight::setShadowView(glm::vec3 dim, glm::vec3 center) { m_viewMatrices[0] = glm::lookAt(center, center-m_direction, glm::vec3(0, 1, 0)); m_projectionMatrices[0] = glm::ortho(-dim.x/2, dim.x/2, -dim.y/2, dim.y/2, -dim.z/2, dim.z/2); } void DirectionnalLight::setCascadedShadowView(Camera *camera) { // TODO find projection bounding boxes and define the 3 shadowmap views } void DirectionnalLight::updateShadowMap(Scene* scene) { bool hasCascades = m_projectionMatrices.size() == 3; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glViewport(0, 0, m_shadowMapResolution, m_shadowMapResolution); m_shadowMap->bindFBO(); glClearDepth(1.0); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); for(SceneIterator* geometryIt = scene->getGeometry(); geometryIt->isValid(); geometryIt->next()) { GeometryNode* node = geometryIt->getItem(); if(node->mesh->getFlags() & (1 << Mesh::MESH_SHADOWED)) { int shaderId = (node->mesh->getFlags() & (1 << Mesh::MATERIAL_ALPHA_MASK)) != 0; // compute matrix attributes if(hasCascades) { shaderId += 2; m_shaders[shaderId]->bind(); glm::mat4 lightMVP; lightMVP = m_projectionMatrices[0] * (m_viewMatrices[0] * node->modelMatrix); m_shaders[shaderId]->bindMat4(m_shaders[shaderId]->getLocation("frontShadowMVP"), lightMVP); lightMVP = m_projectionMatrices[1] * (m_viewMatrices[1] * node->modelMatrix); m_shaders[shaderId]->bindMat4(m_shaders[shaderId]->getLocation("midShadowMVP"), lightMVP); lightMVP = m_projectionMatrices[2] * (m_viewMatrices[2] * node->modelMatrix); m_shaders[shaderId]->bindMat4(m_shaders[shaderId]->getLocation("backShadowMVP"), lightMVP); node->mesh->draw(m_shaders[shaderId], false, shaderId, false); } else { m_shaders[shaderId]->bind(); glm::mat4 lightMVP = m_projectionMatrices[0] * (m_viewMatrices[0] * node->modelMatrix); m_shaders[shaderId]->bindMat4(m_shaders[shaderId]->getLocation("MVP"), lightMVP); node->mesh->draw(m_shaders[shaderId], false, shaderId, false); } } } } // POINT LIGHT int PointLight::m_shaderRefCounter = 0; Shader* PointLight::m_shaders[2] = {NULL}; PointLight::PointLight(glm::vec3 pos, float range, glm::vec3 lightColor) : m_position(pos), m_range(range), m_shadowCaster(false) { m_color = lightColor; m_shadowMap = NULL; } void PointLight::bindAttributes(Shader *shader, Camera *camera) { glm::vec4 posInView = camera->getViewMatrix() * glm::vec4(m_position.x, m_position.y, m_position.z, 1.f); shader->bindVec3(shader->getLocation("lightColor"), m_color); shader->bindVec3(shader->getLocation("pointLight"), glm::vec3(posInView)); shader->bindFloat(shader->getLocation("range"), m_range); if(m_shadowCaster) { m_shadowMap->getTexture(0)->bind(7); // TODO use something else than 7 shader->bindInteger(shader->getLocation("shadowMap"), 7); shader->bindMat3(shader->getLocation("inverseViewMatrix"), glm::inverse(glm::mat3(camera->getViewMatrix()))); } } unsigned int PointLight::getFlags() { unsigned int flag = 1 << POINT_FLAG; if(m_shadowCaster) flag |= 1 << SHADOWMAP_FLAG; return flag; } void PointLight::initShadowMap(int resolution) { m_shadowMapResolution = resolution; setPos(m_position); // shader compilation if(m_shaderRefCounter == 0) { ShaderSource source; Resource::ResourceMap shaderMap; Resource::getResourcePack_shaders(shaderMap); source.setSource(shaderMap["shaders/shadow.vert.glsl"], ShaderSource::VERTEX); source.setSource(shaderMap["shaders/shadow.geom.glsl"], ShaderSource::GEOMETRY); source.setSource(shaderMap["shaders/shadow.frag.glsl"], ShaderSource::FRAGMENT); unsigned int flag = 1 << POINT_FLAG; m_shaders[0] = source.compile(Mesh::MESH_3D, flag); m_shaders[1] = source.compile(Mesh::MESH_3D & Mesh::MATERIAL_ALPHA_MASK, flag); } if(!m_shadowCaster) ++m_shaderRefCounter; m_shadowCaster = true; // Depth buffer Texture *tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, resolution, resolution, GL_FLOAT, GL_TEXTURE_CUBE_MAP); tex->setParameter(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); tex->setParameter(GL_TEXTURE_COMPARE_FUNC, GL_LESS); // framebuffer m_shadowCaster = true; m_shadowMap = new FrameBuffer(); m_shadowMap->addTexture(tex, GL_DEPTH_ATTACHMENT); m_shadowMap->initColorAttachments(); } void PointLight::updateTransform() { if(m_shadowCaster) { m_shadowTransforms.clear(); glm::mat4 shadowProj = glm::perspective(45.0f, 1.f, 0.01f, m_range); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0))); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(-1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0))); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,1.0,0.0), glm::vec3(0.0,0.0,1.0))); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,-1.0,0.0), glm::vec3(0.0,0.0,-1.0))); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,0.0,1.0), glm::vec3(0.0,-1.0,0.0))); m_shadowTransforms.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,0.0,-1.0), glm::vec3(0.0,-1.0,0.0))); } } void PointLight::destroyShadowMap() { if(m_shadowCaster) { m_shadowCaster = false; --m_shaderRefCounter; if(m_shaderRefCounter == 0) { for(int i=0; i<2; ++i) delete m_shaders[i]; } if(m_shadowMap != NULL) delete m_shadowMap; } m_shadowMap = NULL; } void PointLight::updateShadowMap(Scene* scene) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); m_shadowMap->bindFBO(); glViewport(0, 0, m_shadowMapResolution, m_shadowMapResolution); glClearDepth(1.0); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); for(SceneIterator* geometryIt = scene->getGeometry(); geometryIt->isValid(); geometryIt->next()) { GeometryNode* node = geometryIt->getItem(); if(node->mesh->getFlags() & (1 << Mesh::MESH_SHADOWED)) { int shaderId = (node->mesh->getFlags() & (1 << Mesh::MATERIAL_ALPHA_MASK)) != 0; m_shaders[shaderId]->bind(); m_shaders[shaderId]->bindMat4(m_shaders[shaderId]->getLocation("modelMatrix"), node->modelMatrix); m_shaders[shaderId]->bindMat4Array(m_shaders[shaderId]->getLocation("layerTransform"), m_shadowTransforms.data(), m_shadowTransforms.size()); m_shaders[shaderId]->bindFloat(m_shaders[shaderId]->getLocation("far_plane"), m_range); node->mesh->draw(m_shaders[shaderId], false, shaderId, false); } } FrameBuffer::screen->bindFBO(); } // OLD IMPLEMENTATION /* void Light::initShadowMap(int resolution, glm::vec3 dim) { m_shadowMapResolution = resolution; Texture* tex; switch(m_type) { case DIRECTIONNAL: // 3 orthogonal matrices (cascaded shadowmaps) // 3 square textures m_viewMatrix.push_back(glm::lookAt(m_position, m_position-m_direction, glm::vec3(0, 1, 0))); m_projectionMatrix = glm::ortho(-dim.x/2, dim.x/2, -dim.y/2, dim.y/2, -dim.z/2, dim.z/2); tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, resolution, resolution, GL_FLOAT, GL_TEXTURE_2D); break; case POINT: // see http://learnopengl.com/#!Advanced-Lighting/Shadows/Point-Shadows // 6 projection matrices // 1 cubemap texture m_viewMatrix = glm::lookAt(m_position, m_position-glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)); m_projectionMatrix = glm::perspective(90.f, 1.f, 0.1f, 100.f); tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, resolution, resolution, GL_FLOAT, GL_TEXTURE_CUBE_MAP); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0)); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(-1.0,0.0,0.0), glm::vec3(0.0,-1.0,0.0)); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,1.0,0.0), glm::vec3(0.0,0.0,1.0)); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,-1.0,0.0), glm::vec3(0.0,0.0,-1.0)); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,0.0,1.0), glm::vec3(0.0,-1.0,0.0)); m_viewMatrices.push_back(shadowProj * glm::lookAt(m_position, m_position + glm::vec3(0.0,0.0,-1.0), glm::vec3(0.0,-1.0,0.0)); break; case SPOT: // 1 projection matrix // 1 square texture m_viewMatrix.push_back(glm::lookAt(m_position, m_position+m_direction, glm::vec3(0, 1, 0))); m_projectionMatrix = glm::perspective(m_cutOffAngle, 1.f, 0.1f, 100.f); tex = new Texture(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, resolution, resolution, GL_FLOAT, GL_TEXTURE_2D); break; case AMBIENT: return; } // shader compilation // TODO : do not compile shader for every light ShaderSource source; Resource::ResourceMap shaderMap; Resource::getResourcePack_shaders(shaderMap); source.setSource(shaderMap["shaders/shadow.vert.glsl"], ShaderSource::VERTEX); if(m_type == POINT) source.setSource(shaderMap["shaders/shadow.geom.glsl"], ShaderSource::GEOMETRY); source.setSource(shaderMap["shaders/shadow.frag.glsl"], ShaderSource::FRAGMENT); m_shaders[0] = source.compile(Mesh::MESH_3D, getFlags()); m_shaders[1] = source.compile(Mesh::MESH_3D & Mesh::MATERIAL_ALPHA_MASK, getFlags()); // Depth buffer tex->setFiltering(GL_LINEAR); tex->setWrap(GL_CLAMP_TO_EDGE); tex->setParameter(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); tex->setParameter(GL_TEXTURE_COMPARE_FUNC, GL_LESS); // framebuffer m_shadowCaster = true; m_shadowMap = new FrameBuffer(); m_shadowMap->addTexture(tex, GL_DEPTH_ATTACHMENT); m_shadowMap->initColorAttachments(); } void Light::generateShadowMap(Scene* scene) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glViewport(0, 0, m_shadowMapResolution, m_shadowMapResolution); m_shadowMap->bindFBO(); glClearDepth(1.0); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // glCullFace(GL_FRONT); for(SceneIterator* geometryIt = scene->getGeometry(); geometryIt->isValid(); geometryIt->next()) { GeometryNode* node = geometryIt->getItem(); if(node->mesh->getFlags() & (1 << Mesh::MESH_SHADOWED)) { // compute matrix attributes glm::mat4 lightMVP = getProjectionMatrix() * (getViewMatrix() * node->modelMatrix); int hasAlpha = (node->mesh->getFlags() & (1 << Mesh::MATERIAL_ALPHA_MASK)) > 0; m_shaders[hasAlpha]->bind(); m_shaders[hasAlpha]->bindMat4(m_shaders[hasAlpha]->getLocation("MVP"), lightMVP); node->mesh->draw(m_shaders[hasAlpha], false, hasAlpha, false); } } // glCullFace(GL_BACK); } void Light::bindShadowMap(Shader *shader) { glm::mat4 lightMVP = Light::biasMatrix * m_projectionMatrix * light->getViewMatrix() * node->modelMatrix; shader->bindMat4(shader->getLocation("lightMVP"), lightMVP); } Texture* Light::getShadowMapTexture() { return m_shadowMap->getTexture(0); } void Light::setPosition(glm::vec3 new_pos) { m_position = new_pos; m_viewMatrix = glm::lookAt(m_position, m_position+m_direction, glm::vec3(0, 1, 0)); } unsigned int Light::getFlags() { unsigned int flags = 0; switch(m_type) { case AMBIENT : flags = 1 << AMBIENT_FLAG; break; case DIRECTIONNAL : flags = 1 << DIRECTIONNAL_FLAG; break; case POINT : flags = 1 << POINT_FLAG; break; case SPOT : flags = 1 << SPOT_FLAG; break; } if(m_shadowCaster) flags |= 1 << SHADOWMAP_FLAG; return flags; } */