/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #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 (h_width, std::fabs(view_left.Dot(dt))); h_height = GS::Types::Max (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(); } //------------------------------------------------------------------------------