Files
Webcam/include/engine/gpu/gpu_shadow_map.cpp

377 lines
14 KiB
C++
Raw Permalink Blame History

/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "gpu/gpu_renderer.h"
#include "core/light.h"
#include "log/log.h"
using namespace GS::Core;
using namespace GS::GPU;
struct UVOffset
{ float x, y; };
//------------------------------------------------------------------------------
void Renderer::CreateShadowMaps()
{
gpu_config.enable_shadow = registry.GetBool("ShadowMapping:Enable", true);
gpu_config.shadow_size = registry.GetInteger("ShadowMapping:Size", 1024);
__LOG__ << "Creating shadow maps (" << gpu_config.shadow_size << "x" << gpu_config.shadow_size << ").\n";
if (gpu_config.enable_shadow)
{
shadow_map = NewTexture("shadow_map");
shadow_map->Create(NULL, gpu_config.shadow_size, gpu_config.shadow_size, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
shadow_map->ConfigureAsShadowMap();
shadow_map_fbo->SetDepthTexture(shadow_map);
}
}
void Renderer::FreeShadowMaps()
{
shadow_map = NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
struct ShadowRenderData : public Light::RenderData::ShadowRenderData
{
Renderer::ShadowMapSplit split;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static float ComputeSplitZBoundary(const Camera &lod_view, const Light &light, int index, int count)
{
if (index <= 0)
return lod_view.GetNearClippingPlane();
float k = float(index) / count;
float z_log = lod_view.GetNearClippingPlane() * powf(light.shadow_range / lod_view.GetNearClippingPlane(), k);
float z_lin = lod_view.GetNearClippingPlane() + (light.shadow_range - lod_view.GetNearClippingPlane()) * k;
return z_log * light.shadow_distribution + z_lin * (1.f - light.shadow_distribution);
}
static void FitViewToFrustum(Camera &view, const GS::Frustum &slice, float d)
{
using namespace GS;
/*
Compute split view item properties so that the split view frustum act as
a perfect fit for the split frustum.
*/
const Vector4 *fv = slice.GetVertices();
Vector4 vfv[8];
view.GetInverseMatrix().Apply(vfv, fv, 8);
const Matrix4 &m = view.GetMatrix();
Vector4 mm[2];
mm[0] = mm[1] = vfv[0];
for (int n = 1; n < 8; ++n)
{
mm[0] = Vector4::Minimum(mm[0], vfv[n]);
mm[1] = Vector4::Maximum(mm[1], vfv[n]);
}
Vector4 focus = ((mm[0] + mm[1]) * 0.5) * m;
// Position the shadow map light item,
view.SetPosition(focus - m.GetRow(2) * d);
// View clipping planes.
Vector4 view_front = m.GetRow(2);
view.z_far = view_front.Dot(fv[0] - view.GetPosition());
for (int n = 1; n < 8; ++n)
view.z_far = GS::Types::Max(view.z_far, view_front.Dot(fv[n] - view.GetPosition()));
// View dimensions.
Vector4 view_left = view.GetMatrix().GetRow(0),
view_top = view.GetMatrix().GetRow(1);
Vector4 dt = fv[0] - view.GetPosition();
float h_width = std::fabs(view_left.Dot(dt)),
h_height = std::fabs(view_top.Dot(dt));
for (int n = 1; n < 8; ++n)
{
dt = fv[n] - view.GetPosition();
h_width = GS::Types::Max <float> (h_width, std::fabs(view_left.Dot(dt)));
h_height = GS::Types::Max <float> (h_height, std::fabs(view_top.Dot(dt)));
}
view.ortho_w = h_width * 2.f;
view.ortho_h = h_height * 2.f;
}
bool Renderer::LightPreparePSSM(const Light &l, bool build_dlist) const
{
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
if (!light_render_data || !light_render_data->shadow_data.Allocate(4))
return false;
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
for (int n = 0; n < split_count; ++n)
{
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
if (data.render_data.IsNull())
data.render_data = new ShadowRenderData;
ShadowRenderData *pssm_data = (ShadowRenderData *)data.render_data.c_ptr();
ShadowMapSplit &split = pssm_data->split;
static UVOffset split_offset[] = { { 0.0, 0.0 }, { 0.5, 0.0 }, { 0.0, 0.5 }, { 0.5, 0.5 } };
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 2.f, gpu_config.shadow_size / 2.f);
// Compute frustum slice.
Frustum slice;
float znear = ComputeSplitZBoundary(*view_item, l, n, split_count), zfar = ComputeSplitZBoundary(*view_item, l, n + 1, split_count);
view_item->ComputeFrustum(slice, GetViewport(), znear, zfar);
// Adjust view item.
split.view.is_orthographic = true;
split.view.aspect_ratio = 1;
split.view.SnapshotTransformation(l.GetMatrix());
FitViewToFrustum(split.view, slice, l.shadow_range * 4.f);
split.view.ComputeFrustum(split.view.frustum, split.rect);
// Cull.
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store slice view to light matrix.
data.slice_distance = zfar;
data.imatrix = split.view.GetInverseMatrix();
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix);
if (!gpu_config.tex_origin_is_top_left)
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data.pmatrix;
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
// Crop on PSSM region.
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.0)) * Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 1.0)) * data.pmatrix;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderPSSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
for (int n = 0; n < split_count; ++n)
{
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
SetViewport(shd_data->split.rect);
SetClippingRect(&shd_data->split.rect);
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
view_item = &shd_data->split.view;
ApplyCamera();
DrawList(shd_data->split.list.display_lists[0], dc);
DrawList(shd_data->split.list.display_lists[2], dc);
shd_data->split.list.Clear(false); // limit dynamic allocations
}
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::LightPreparePSM(const Light &l, bool build_dlist) const
{
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
if (!light_render_data || !light_render_data->shadow_data.Allocate(6))
return false;
for (uint n = 0; n < 6; ++n)
{
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
if (data.render_data.IsNull())
data.render_data = new ShadowRenderData;
ShadowRenderData *shd_data = (ShadowRenderData *)data.render_data.c_ptr();
ShadowMapSplit &split = shd_data->split;
static Vector4 view_angle[6] = { Vector4(0, 0, 0), Vector4(0, Units::Deg(90.f), 0), Vector4(0, Units::Deg(180.f), 0), Vector4(0, Units::Deg(270.f), 0), Vector4(Units::Deg(90.f), 0, 0), Vector4(Units::Deg(270.f), 0, 0) };
static UVOffset split_offset[] = { { 0, 0 }, { 0.33f, 0 }, { 0.66f, 0 }, { 0, 0.5f }, { 0.33f, 0.5f }, { 0.66f, 0.5f } };
split.view.aspect_ratio = 1;
split.view.SetFov(Units::Deg(95.f)); // Add a 5<> safe area to avoid artifacts at transition.
split.view.SnapshotTransformation(l.GetMatrix() * Matrix4::FromMatrix3(Matrix3::FromEuler(view_angle[n])));
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 3.f, gpu_config.shadow_size / 2.f);
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store cube view to light matrix.
data.imatrix = split.view.GetInverseMatrix();
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix); // FIXME watch out, do we need the split viewport (split.rect) or the complete viewport???
if (!gpu_config.tex_origin_is_top_left)
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1, -1, 1)) * data.pmatrix;
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5f, 0.5f, 0.5f)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
// Crop on view region.
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.f)) * Matrix4::ScaleMatrix(Vector4(0.33f, 0.5f, 1)) * data.pmatrix;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderPSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
for (uint n = 0; n < 6; ++n)
{
ShadowRenderData *psm_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
SetViewport(psm_data->split.rect);
SetClippingRect(&psm_data->split.rect);
view_item = &psm_data->split.view;
ApplyCamera();
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
DrawList(psm_data->split.list.display_lists[0], dc);
DrawList(psm_data->split.list.display_lists[2], dc);
psm_data->split.list.Clear(false); // limit dynamic allocations
}
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::LightPrepareSSM(const Light &l, bool build_dlist) const
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
if (!data || !data->shadow_data.Allocate(1))
return false;
if (data->shadow_data[0].render_data.IsNull())
data->shadow_data[0].render_data = new ShadowRenderData;
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
Renderer::ShadowMapSplit &split = shd_data->split;
split.view.AlignTo(l);
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store shadow matrices.
data->shadow_data[0].imatrix = split.view.GetInverseMatrix();
l.ComputeProjectionMatrix(data->shadow_data[0].pmatrix);
if (!gpu_config.tex_origin_is_top_left)
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data->shadow_data[0].pmatrix;
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data->shadow_data[0].pmatrix;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderSSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
ShadowRenderData *render_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
view_item = &render_data->split.view;
ApplyCamera();
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
DrawList(render_data->split.list.display_lists[0], dc);
DrawList(render_data->split.list.display_lists[2], dc);
render_data->split.list.Clear(false); // limit dynamic allocations
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Renderer::ViewConfig Renderer::BackupView() const
{
return ViewConfig(view_item, viewport);
}
void Renderer::RestoreViewport(const ViewConfig &config)
{
SetViewport(config.viewport);
}
void Renderer::RestoreView(const ViewConfig &config)
{
RestoreViewport(config);
view_item = config.camera;
ApplyCamera();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::PrepareShadowMap(const Light &l, bool build_dlist) const
{
switch (l.model)
{
case Light::Model_Spot: return LightPrepareSSM(l, build_dlist);
case Light::Model_Point: return LightPreparePSM(l, build_dlist);
case Light::Model_Linear: return LightPreparePSSM(l, build_dlist);
}
return false;
}
void Renderer::RenderShadowMap(Light &l)
{
PerfBeginEvent(__FUNCTION__, Color::Blue);
SetCurrentFBO(shadow_map_fbo);
SetCullFunc(CullBack);
fRect old_clipping = GetClippingRect();
SetClippingRect(NULL);
switch (l.model)
{
case Light::Model_Spot: LightRenderSSM(l); break;
case Light::Model_Point: LightRenderPSM(l); break;
case Light::Model_Linear: LightRenderPSSM(l); break;
}
SetClippingRect(&old_clipping);
SetCullFunc(CullFront);
SetCurrentFBO(NULL);
PerfEndEvent();
}
//------------------------------------------------------------------------------