SparrowRenderer/src/light.cpp

502 lines
18 KiB
C++

#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 <resource.h>
#include <glm/ext.hpp>
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<GeometryNode*>* 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<GeometryNode*>* 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<GeometryNode*>* 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;
}
*/