Program Listing for File DirectionalLight.cpp
↰ Return to documentation for file (Src/GraphicsEngineOpenGL/scene/light/directional_light/DirectionalLight.cpp
)
#include "scene/light/directional_light/DirectionalLight.hpp"
DirectionalLight::DirectionalLight()
:
Light(), shadow_map(std::make_shared<CascadedShadowMap>()),
direction(glm::vec3{ 0, 0, 0 }),
shadow_near_plane(0.f), shadow_far_plane(0.f),
cascade_light_matrices(NUM_CASCADES, glm::mat4(0.f))
{
light_proj = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, 0.1f, 100.f);
}
DirectionalLight::DirectionalLight(GLuint shadow_width,
GLuint shadow_height,
GLfloat red,
GLfloat green,
GLfloat blue,
GLfloat radiance,
GLfloat x_dir,
GLfloat y_dir,
GLfloat z_dir,
GLfloat near_plane,
GLfloat far_plane,
int num_cascades)
:
Light(red, green, blue, radiance), shadow_map(std::make_shared<CascadedShadowMap>()),
direction(glm::vec3{ x_dir, y_dir, z_dir }),
shadow_near_plane(near_plane), shadow_far_plane(far_plane),
cascade_light_matrices(NUM_CASCADES, glm::mat4(0.f))
{
light_proj = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, 0.1f, 100.f);
shadow_map->init(shadow_width, shadow_height, num_cascades);
}
glm::mat4 DirectionalLight::get_light_view_matrix() const
{
return glm::lookAt(direction, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
}
glm::vec3 DirectionalLight::get_direction() const { return direction; }
glm::vec3 DirectionalLight::get_color() const { return color; }
float DirectionalLight::get_radiance() const { return radiance; }
std::vector<GLfloat> DirectionalLight::get_cascaded_slots() const
{
std::vector<GLfloat> result;
for (int i = 0; i < NUM_CASCADES + 1; i++) { result.push_back(cascade_slots[i]); }
return result;
}
std::vector<glm::mat4> &DirectionalLight::get_cascaded_light_matrices() { return cascade_light_matrices; }
void DirectionalLight::update_shadow_map(GLfloat shadow_width, GLfloat shadow_height, GLuint num_cascades)
{
shadow_map.reset(new CascadedShadowMap);
shadow_map->init((GLuint)shadow_width, (GLuint)shadow_height, num_cascades);
}
std::vector<glm::vec4> DirectionalLight::get_frustum_corners_world_space(const glm::mat4 &proj, const glm::mat4 &view)
{
const auto inv = glm::inverse(proj * view);
std::vector<glm::vec4> frustumCorners;
for (unsigned int x = 0; x < 2; ++x) {
for (unsigned int y = 0; y < 2; ++y) {
for (unsigned int z = 0; z < 2; ++z) {
const glm::vec4 pt = inv * glm::vec4(2.0f * x - 1.0f, 2.0f * y - 1.0f, 2.0f * z - 1.0f, 1.0f);
frustumCorners.push_back(pt / pt.w);
}
}
}
return frustumCorners;
}
void DirectionalLight::calc_cascaded_slots()
{
GLuint number_of_elements = shadow_map->get_num_active_cascades();
for (int i = 0; i < NUM_CASCADES + 1; i++) { cascade_slots[i] = 100000.f; }
for (int i = 0; i < static_cast<int>(number_of_elements + 1); i++) {
if (i == 0) {
(cascade_slots)[i] = shadow_near_plane;
} else {
(cascade_slots)[i] = (shadow_far_plane) * ((GLfloat)i / (GLfloat)(number_of_elements));
}
}
// cascade_slots = { shadow_near_plane, shadow_far_plane / 50.f,
// shadow_far_plane / 25.f, shadow_far_plane };
return;
}
void DirectionalLight::calc_orthogonal_projections(glm::mat4 camera_view_matrix,
GLfloat fov,
GLuint window_width,
GLuint window_height,
GLuint current_num_cascades)
{
// calc the start and end point for our cascaded shadow maps
calc_cascaded_slots();
for (int i = 0; i < static_cast<int>(current_num_cascades); i++) {
glm::mat4 curr_cascade_proj = glm::perspective(
glm::radians(fov), (float)window_width / (float)window_height, cascade_slots[i], cascade_slots[i + 1]);
std::vector<glm::vec4> frustumCornerWorldSpace =
get_frustum_corners_world_space(curr_cascade_proj, camera_view_matrix);
glm::vec3 center = glm::vec3(0, 0, 0);
for (const auto &v : frustumCornerWorldSpace) { center += glm::vec3(v); }
center /= frustumCornerWorldSpace.size();
glm::mat4 light_view_matrix = glm::lookAt(center - get_direction(), center, glm::vec3(0.0f, 1.0f, 0.0f));
// the # of frustum corners = 8
GLfloat minX = std::numeric_limits<float>::max();
GLfloat maxX = std::numeric_limits<float>::min();
GLfloat minY = std::numeric_limits<float>::max();
GLfloat maxY = std::numeric_limits<float>::min();
GLfloat minZ = std::numeric_limits<float>::max();
GLfloat maxZ = std::numeric_limits<float>::min();
for (unsigned int m = 0; m < frustumCornerWorldSpace.size(); m++) {
// transform each corner from view to world space
glm::vec4 v_light_view = light_view_matrix * frustumCornerWorldSpace[m];
// now go to light space
minX = std::min(minX, v_light_view.x);
maxX = std::max(maxX, v_light_view.x);
// we always have negativ y values
minY = std::min(minY, v_light_view.y);
maxY = std::max(maxY, v_light_view.y);
minZ = std::min(minZ, v_light_view.z);
maxZ = std::max(maxZ, v_light_view.z);
}
// Tune this parameter according to the scene
// for having objects casting shadows that are actually not in the frustum
// :)
constexpr float zMult = 10.0f;
if (minZ < 0) {
minZ *= zMult;
} else {
minZ /= zMult;
}
if (maxZ < 0) {
maxZ /= zMult;
} else {
maxZ *= zMult;
}
glm::mat4 light_projection = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);
cascade_light_matrices[i] = light_projection * light_view_matrix;
}
}
glm::mat4 DirectionalLight::calculate_light_transform() { return light_proj * get_light_view_matrix(); }
void DirectionalLight::set_direction(glm::vec3 direction) { this->direction = direction; }
void DirectionalLight::set_radiance(float radiance) { this->radiance = radiance; }
void DirectionalLight::set_color(glm::vec3 color) { this->color = color; }
DirectionalLight::~DirectionalLight() {}