377 lines
14 KiB
C++
377 lines
14 KiB
C++
/* -----------------------------------------------------------------------------
|
||
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();
|
||
}
|
||
//------------------------------------------------------------------------------
|