]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Distribute shadow map update over multiple frames to reduce stutter (#11422)
authorx2048 <codeforsmile@gmail.com>
Sun, 25 Jul 2021 10:36:23 +0000 (12:36 +0200)
committerGitHub <noreply@github.com>
Sun, 25 Jul 2021 10:36:23 +0000 (12:36 +0200)
Reduces stutter and freezes when playing.

 * Maintains double SM and SM Color textures
 * Light frustum update triggers incremental generation of shadow map into secondary 'future' textures.
 * Every incremental update renders a portion of the shadow draw list (split equally).
 * After defined number of frames (currently, 4), 'future' and 'current' textures are swapped, and DirectionalLight 'commits' the new frustum to use when rendering shadows on screen.

Co-authored-by: sfan5 <sfan5@live.de>
builtin/settingtypes.txt
client/shaders/nodes_shader/opengl_fragment.glsl
src/client/clientmap.cpp
src/client/clientmap.h
src/client/game.cpp
src/client/shadows/dynamicshadows.cpp
src/client/shadows/dynamicshadows.h
src/client/shadows/dynamicshadowsrender.cpp
src/client/shadows/dynamicshadowsrender.h
src/defaultsettings.cpp

index 17843fac810201dc6d6e06ad91dd657f2fd2237f..420e9d49c9086b08eef84bb8c2fd22af4f3d1985 100644 (file)
@@ -618,11 +618,11 @@ shadow_filters (Shadow filter quality) enum 1 0,1,2
 #    On true translucent nodes cast colored shadows. This is expensive.
 shadow_map_color (Colored shadows) bool false
 
-
-#    Set the shadow update time, in seconds.
-#    Lower value means shadows and map updates faster, but it consumes more resources.
-#    Minimum value: 0.001; maximum value: 0.2
-shadow_update_time (Map update time) float 0.2 0.001 0.2
+#    Spread a complete update of shadow map over given amount of frames.
+#    Higher values might make shadows laggy, lower values
+#    will consume more resources.
+#    Minimum value: 1; maximum value: 16
+shadow_update_frames (Map shadows update frames) int 8 1 16
 
 #    Set the soft shadow radius size.
 #    Lower values mean sharper shadows, bigger values mean softer shadows.
index 64a88ebbb28b1626e35632072eb0bd4c5b1a6f84..f85ca7b4813a857690187d2975b8cfe1fa6fcf81 100644 (file)
@@ -197,7 +197,7 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist
        float pointDepth;
        float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
 
-       float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
+       float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
        int n = 0;
 
        for (y = -bound; y <= bound; y += 1.0)
@@ -304,7 +304,7 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
        float perspectiveFactor;
 
        float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES));
+       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
@@ -334,7 +334,7 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
        float perspectiveFactor;
 
        float texture_size = 1.0 / (f_textureresolution * 0.5);
-       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES));
+       int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
        int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
        int end_offset = int(samples) + init_offset;
 
@@ -370,7 +370,7 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
 
        float texture_size = 1.0 / (f_textureresolution * 0.5);
        float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
+       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
        int n = 0;
 
        // basic PCF filter
@@ -402,7 +402,7 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 
        float texture_size = 1.0 / (f_textureresolution * 0.5);
        float y, x;
-       float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
+       float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
        int n = 0;
 
        // basic PCF filter
index 8b09eade1400c6ce5eafc1bbbfe7422976392231..77f3b9fe8acce660124e9769970679c87a7cda38 100644 (file)
@@ -636,7 +636,7 @@ void ClientMap::PrintInfo(std::ostream &out)
 }
 
 void ClientMap::renderMapShadows(video::IVideoDriver *driver,
-               const video::SMaterial &material, s32 pass)
+               const video::SMaterial &material, s32 pass, int frame, int total_frames)
 {
        bool is_transparent_pass = pass != scene::ESNRP_SOLID;
        std::string prefix;
@@ -650,7 +650,23 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
 
        MeshBufListList drawbufs;
 
+       int count = 0;
+       int low_bound = is_transparent_pass ? 0 : m_drawlist_shadow.size() / total_frames * frame;
+       int high_bound = is_transparent_pass ? m_drawlist_shadow.size() : m_drawlist_shadow.size() / total_frames * (frame + 1);
+
+       // transparent pass should be rendered in one go
+       if (is_transparent_pass && frame != total_frames - 1) {
+               return;
+       }
+
        for (auto &i : m_drawlist_shadow) {
+               // only process specific part of the list & break early
+               ++count;
+               if (count <= low_bound)
+                       continue;
+               if (count > high_bound)
+                       break;
+
                v3s16 block_pos = i.first;
                MapBlock *block = i.second;
 
@@ -705,6 +721,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
                                local_material.MaterialType = material.MaterialType;
                                local_material.BackfaceCulling = material.BackfaceCulling;
                                local_material.FrontfaceCulling = material.FrontfaceCulling;
+                               local_material.BlendOperation = material.BlendOperation;
                                local_material.Lighting = false;
                                driver->setMaterial(local_material);
 
@@ -720,6 +737,12 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
                }
        }
 
+       // restore the driver material state 
+       video::SMaterial clean;
+       clean.BlendOperation = video::EBO_ADD;
+       driver->setMaterial(clean); // reset material to defaults
+       driver->draw3DLine(v3f(), v3f(), video::SColor(0));
+       
        g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
        g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
        g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
index 93ade4c1520dafc7a5ebff45f486118c6318b915..97ce8d355f2d2a3fd3965476d59075ce3d887d7c 100644 (file)
@@ -125,7 +125,7 @@ class ClientMap : public Map, public scene::ISceneNode
        void renderMap(video::IVideoDriver* driver, s32 pass);
 
        void renderMapShadows(video::IVideoDriver *driver,
-                       const video::SMaterial &material, s32 pass);
+                       const video::SMaterial &material, s32 pass, int frame, int total_frames);
 
        int getBackgroundBrightness(float max_d, u32 daylight_factor,
                        int oldvalue, bool *sunlight_seen_result);
index 85dd8f4bbd9d5f90c9def68cd792bd349375a789..6fc57c8ccca7a49e9413d7207b0d836c8b5979fa 100644 (file)
@@ -609,7 +609,6 @@ struct GameRunData {
        float jump_timer;
        float damage_flash;
        float update_draw_list_timer;
-       float update_shadows_timer;
 
        f32 fog_range;
 
@@ -3881,10 +3880,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
                changed much
        */
        runData.update_draw_list_timer += dtime;
-       runData.update_shadows_timer += dtime;
 
        float update_draw_list_delta = 0.2f;
-       bool draw_list_updated = false;
 
        v3f camera_direction = camera->getDirection();
        if (runData.update_draw_list_timer >= update_draw_list_delta
@@ -3894,18 +3891,10 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
                runData.update_draw_list_timer = 0;
                client->getEnv().getClientMap().updateDrawList();
                runData.update_draw_list_last_cam_dir = camera_direction;
-               draw_list_updated = true;
        }
 
-       if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
-               update_draw_list_delta = shadow->getUpdateDelta();
-
-               if (m_camera_offset_changed ||
-                               (runData.update_shadows_timer > update_draw_list_delta &&
-                               (!draw_list_updated || shadow->getDirectionalLightCount() == 0))) {
-                       runData.update_shadows_timer = 0;
-                       updateShadows();
-               }
+       if (RenderingEngine::get_shadow_renderer()) {
+               updateShadows();
        }
 
        m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
@@ -4062,7 +4051,7 @@ void Game::updateShadows()
        shadow->getDirectionalLight().setDirection(sun_pos);
        shadow->setTimeOfDay(in_timeofday);
 
-       shadow->getDirectionalLight().update_frustum(camera, client);
+       shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
 }
 
 /****************************************************************************
index 17b711a61a9be881e006fac143512d9ccae43770..0c7eea0e74339a6a39a71615f596645c8652ba3e 100644 (file)
@@ -38,8 +38,8 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
        float tanFovX = tanf(cam->getFovX() * 0.5f);
 
        // adjusted frustum boundaries
-       float sfNear = shadow_frustum.zNear;
-       float sfFar = adjustDist(shadow_frustum.zFar, cam->getFovY());
+       float sfNear = future_frustum.zNear;
+       float sfFar = adjustDist(future_frustum.zFar, cam->getFovY());
 
        // adjusted camera positions
        v3f camPos2 = cam->getPosition();
@@ -87,14 +87,15 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
        v3f eye_displacement = direction * vvolume;
 
        // we must compute the viewmat with the position - the camera offset
-       // but the shadow_frustum position must be the actual world position
+       // but the future_frustum position must be the actual world position
        v3f eye = frustumCenter - eye_displacement;
-       shadow_frustum.position = world_center - eye_displacement;
-       shadow_frustum.length = vvolume;
-       shadow_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
-       shadow_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(shadow_frustum.length,
-                       shadow_frustum.length, -shadow_frustum.length,
-                       shadow_frustum.length,false);
+       future_frustum.position = world_center - eye_displacement;
+       future_frustum.length = vvolume;
+       future_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
+       future_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(future_frustum.length,
+                       future_frustum.length, -future_frustum.length,
+                       future_frustum.length,false);
+       future_frustum.camera_offset = cam->getOffset();
 }
 
 DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
@@ -104,23 +105,44 @@ DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
                farPlane(farValue), mapRes(shadowMapResolution), pos(position)
 {}
 
-void DirectionalLight::update_frustum(const Camera *cam, Client *client)
+void DirectionalLight::update_frustum(const Camera *cam, Client *client, bool force)
 {
-       should_update_map_shadow = true;
+       if (dirty && !force)
+               return;
+
        float zNear = cam->getCameraNode()->getNearValue();
        float zFar = getMaxFarValue();
 
        ///////////////////////////////////
        // update splits near and fars
-       shadow_frustum.zNear = zNear;
-       shadow_frustum.zFar = zFar;
+       future_frustum.zNear = zNear;
+       future_frustum.zFar = zFar;
 
        // update shadow frustum
        createSplitMatrices(cam);
        // get the draw list for shadows
        client->getEnv().getClientMap().updateDrawListShadow(
-                       getPosition(), getDirection(), shadow_frustum.length);
+                       getPosition(), getDirection(), future_frustum.length);
        should_update_map_shadow = true;
+       dirty = true;
+
+       // when camera offset changes, adjust the current frustum view matrix to avoid flicker
+       v3s16 cam_offset = cam->getOffset();
+       if (cam_offset != shadow_frustum.camera_offset) {
+               v3f rotated_offset;
+               shadow_frustum.ViewMat.rotateVect(rotated_offset, intToFloat(cam_offset - shadow_frustum.camera_offset, BS));
+               shadow_frustum.ViewMat.setTranslation(shadow_frustum.ViewMat.getTranslation() + rotated_offset);
+               shadow_frustum.camera_offset = cam_offset;
+       }
+}
+
+void DirectionalLight::commitFrustum()
+{
+       if (!dirty)
+               return;
+
+       shadow_frustum = future_frustum;
+       dirty = false;
 }
 
 void DirectionalLight::setDirection(v3f dir)
@@ -144,6 +166,16 @@ const m4f &DirectionalLight::getProjectionMatrix() const
        return shadow_frustum.ProjOrthMat;
 }
 
+const m4f &DirectionalLight::getFutureViewMatrix() const
+{
+       return future_frustum.ViewMat;
+}
+
+const m4f &DirectionalLight::getFutureProjectionMatrix() const
+{
+       return future_frustum.ProjOrthMat;
+}
+
 m4f DirectionalLight::getViewProjMatrix()
 {
        return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
index a53612f7c4e6ce647c0b4c52e3bf361cf294312a..d8be66be89bd67032d289553666c12060a647fa5 100644 (file)
@@ -34,6 +34,7 @@ struct shadowFrustum
        core::matrix4 ProjOrthMat;
        core::matrix4 ViewMat;
        v3f position;
+       v3s16 camera_offset;
 };
 
 class DirectionalLight
@@ -47,7 +48,7 @@ class DirectionalLight
 
        //DISABLE_CLASS_COPY(DirectionalLight)
 
-       void update_frustum(const Camera *cam, Client *client);
+       void update_frustum(const Camera *cam, Client *client, bool force = false);
 
        // when set direction is updated to negative normalized(direction)
        void setDirection(v3f dir);
@@ -59,6 +60,8 @@ class DirectionalLight
        /// Gets the light's matrices.
        const core::matrix4 &getViewMatrix() const;
        const core::matrix4 &getProjectionMatrix() const;
+       const core::matrix4 &getFutureViewMatrix() const;
+       const core::matrix4 &getFutureProjectionMatrix() const;
        core::matrix4 getViewProjMatrix();
 
        /// Gets the light's far value.
@@ -88,6 +91,8 @@ class DirectionalLight
 
        bool should_update_map_shadow{true};
 
+       void commitFrustum();
+
 private:
        void createSplitMatrices(const Camera *cam);
 
@@ -99,4 +104,6 @@ class DirectionalLight
        v3f pos;
        v3f direction{0};
        shadowFrustum shadow_frustum;
+       shadowFrustum future_frustum;
+       bool dirty{false};
 };
index 135c9f895fb150433fd03551766886dc9b4de6c1..350586225b2f6fab708035737cb9780255e7da25 100644 (file)
@@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "client/shader.h"
 #include "client/client.h"
 #include "client/clientmap.h"
+#include "profiler.h"
 
 ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
                m_device(device), m_smgr(device->getSceneManager()),
-               m_driver(device->getVideoDriver()), m_client(client)
+               m_driver(device->getVideoDriver()), m_client(client), m_current_frame(0)
 {
        m_shadows_enabled = true;
 
@@ -43,7 +44,7 @@ ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
        m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
        m_shadow_map_colored = g_settings->getBool("shadow_map_color");
        m_shadow_samples = g_settings->getS32("shadow_filters");
-       m_update_delta = g_settings->getFloat("shadow_update_time");
+       m_map_shadow_update_frames = g_settings->getS16("shadow_update_frames");
 }
 
 ShadowRenderer::~ShadowRenderer()
@@ -66,6 +67,9 @@ ShadowRenderer::~ShadowRenderer()
 
        if (shadowMapClientMap)
                m_driver->removeTexture(shadowMapClientMap);
+
+       if (shadowMapClientMapFuture)
+               m_driver->removeTexture(shadowMapClientMapFuture);
 }
 
 void ShadowRenderer::initialize()
@@ -93,11 +97,6 @@ void ShadowRenderer::initialize()
 }
 
 
-float ShadowRenderer::getUpdateDelta() const
-{
-       return m_update_delta;
-}
-
 size_t ShadowRenderer::addDirectionalLight()
 {
        m_light_list.emplace_back(m_shadow_map_texture_size,
@@ -152,10 +151,9 @@ void ShadowRenderer::setClearColor(video::SColor ClearColor)
        m_clear_color = ClearColor;
 }
 
-void ShadowRenderer::update(video::ITexture *outputTarget)
+void ShadowRenderer::updateSMTextures()
 {
        if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
-               m_smgr->drawAll();
                return;
        }
 
@@ -174,6 +172,13 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
                        true);
        }
 
+       if (!shadowMapClientMapFuture && m_map_shadow_update_frames > 1) {
+               shadowMapClientMapFuture = getSMTexture(
+                       std::string("shadow_clientmap_bb_") + itos(m_shadow_map_texture_size),
+                       m_shadow_map_colored ? m_texture_format_color : m_texture_format,
+                       true);
+       }
+
        if (m_shadow_map_colored && !shadowMapTextureColors) {
                shadowMapTextureColors = getSMTexture(
                        std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
@@ -201,7 +206,22 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
        }
 
        if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
-               // for every directional light:
+               bool reset_sm_texture = false;
+
+               // detect if SM should be regenerated
+               for (DirectionalLight &light : m_light_list) {
+                       if (light.should_update_map_shadow) {
+                               light.should_update_map_shadow = false;
+                               m_current_frame = 0;
+                               reset_sm_texture = true;
+                       }
+               }
+
+               video::ITexture* shadowMapTargetTexture = shadowMapClientMapFuture;
+               if (shadowMapTargetTexture == nullptr)
+                       shadowMapTargetTexture = shadowMapClientMap;
+
+               // Update SM incrementally:
                for (DirectionalLight &light : m_light_list) {
                        // Static shader values.
                        m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
@@ -212,22 +232,60 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
                        // Depth texture is available in irrlicth maybe we
                        // should put some gl* fn here
 
-                       if (light.should_update_map_shadow) {
-                               light.should_update_map_shadow = false;
 
-                               m_driver->setRenderTarget(shadowMapClientMap, true, true,
+                       if (m_current_frame < m_map_shadow_update_frames) {
+                               m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true,
                                                video::SColor(255, 255, 255, 255));
-                               renderShadowMap(shadowMapClientMap, light);
-
-                               if (m_shadow_map_colored) {
-                                       m_driver->setRenderTarget(shadowMapTextureColors,
-                                                       true, false, video::SColor(255, 255, 255, 255));
+                               renderShadowMap(shadowMapTargetTexture, light);
+
+                               // Render transparent part in one pass.
+                               // This is also handled in ClientMap.
+                               if (m_current_frame == m_map_shadow_update_frames - 1) {
+                                       if (m_shadow_map_colored) {
+                                               m_driver->setRenderTarget(shadowMapTextureColors,
+                                                               true, false, video::SColor(255, 255, 255, 255));
+                                       }
+                                       renderShadowMap(shadowMapTextureColors, light,
+                                                       scene::ESNRP_TRANSPARENT);
                                }
-                               renderShadowMap(shadowMapTextureColors, light,
-                                               scene::ESNRP_TRANSPARENT);
                                m_driver->setRenderTarget(0, false, false);
                        }
 
+                       reset_sm_texture = false;
+               } // end for lights
+
+               // move to the next section
+               if (m_current_frame <= m_map_shadow_update_frames)
+                       ++m_current_frame;
+
+               // pass finished, swap textures and commit light changes
+               if (m_current_frame == m_map_shadow_update_frames) {
+                       if (shadowMapClientMapFuture != nullptr)
+                               std::swap(shadowMapClientMapFuture, shadowMapClientMap);
+
+                       // Let all lights know that maps are updated
+                       for (DirectionalLight &light : m_light_list)
+                               light.commitFrustum();
+               }
+       }
+}
+
+void ShadowRenderer::update(video::ITexture *outputTarget)
+{
+       if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
+               m_smgr->drawAll();
+               return;
+       }
+
+       updateSMTextures();
+
+       if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
+
+               for (DirectionalLight &light : m_light_list) {
+                       // Static shader values.
+                       m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
+                       m_shadow_depth_cb->MaxFar = (f32)m_shadow_map_max_distance * BS;
+
                        // render shadows for the n0n-map objects.
                        m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
                                        true, video::SColor(255, 255, 255, 255));
@@ -299,8 +357,8 @@ video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name
 void ShadowRenderer::renderShadowMap(video::ITexture *target,
                DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
 {
-       m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
-       m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
+       m_driver->setTransform(video::ETS_VIEW, light.getFutureViewMatrix());
+       m_driver->setTransform(video::ETS_PROJECTION, light.getFutureProjectionMatrix());
 
        // Operate on the client map
        for (const auto &shadow_node : m_shadow_node_array) {
@@ -322,10 +380,13 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
                //material.PolygonOffsetDepthBias = 1.0f/4.0f;
                //material.PolygonOffsetSlopeScale = -1.f;
 
-               if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
+               if (m_shadow_map_colored && pass != scene::ESNRP_SOLID) {
                        material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
-               else
+               }
+               else {
                        material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
+                       material.BlendOperation = video::EBO_MIN;
+               }
 
                // FIXME: I don't think this is needed here
                map_node->OnAnimate(m_device->getTimer()->getTime());
@@ -333,7 +394,7 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
                m_driver->setTransform(video::ETS_WORLD,
                                map_node->getAbsoluteTransformation());
 
-               map_node->renderMapShadows(m_driver, material, pass);
+               map_node->renderMapShadows(m_driver, material, pass, m_current_frame, m_map_shadow_update_frames);
                break;
        }
 }
@@ -354,8 +415,10 @@ void ShadowRenderer::renderShadowObjects(
                u32 n_node_materials = shadow_node.node->getMaterialCount();
                std::vector<s32> BufferMaterialList;
                std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
+               std::vector<video::E_BLEND_OPERATION> BufferBlendOperationList;
                BufferMaterialList.reserve(n_node_materials);
                BufferMaterialCullingList.reserve(n_node_materials);
+               BufferBlendOperationList.reserve(n_node_materials);
 
                // backup materialtype for each material
                // (aka shader)
@@ -365,12 +428,11 @@ void ShadowRenderer::renderShadowObjects(
 
                        BufferMaterialList.push_back(current_mat.MaterialType);
                        current_mat.MaterialType =
-                                       (video::E_MATERIAL_TYPE)depth_shader;
-
-                       current_mat.setTexture(3, shadowMapTextureFinal);
+                                       (video::E_MATERIAL_TYPE)depth_shader_entities;
 
                        BufferMaterialCullingList.emplace_back(
                                (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
+                       BufferBlendOperationList.push_back(current_mat.BlendOperation);
 
                        current_mat.BackfaceCulling = true;
                        current_mat.FrontfaceCulling = false;
@@ -393,6 +455,7 @@ void ShadowRenderer::renderShadowObjects(
 
                        current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
                        current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
+                       current_mat.BlendOperation = BufferBlendOperationList[m];
                }
 
        } // end for caster shadow nodes
@@ -433,7 +496,7 @@ void ShadowRenderer::createShaders()
                                readShaderFile(depth_shader_vs).c_str(), "vertexMain",
                                video::EVST_VS_1_1,
                                readShaderFile(depth_shader_fs).c_str(), "pixelMain",
-                               video::EPST_PS_1_2, m_shadow_depth_cb);
+                               video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
 
                if (depth_shader == -1) {
                        // upsi, something went wrong loading shader.
@@ -449,6 +512,41 @@ void ShadowRenderer::createShaders()
                m_driver->getMaterialRenderer(depth_shader)->grab();
        }
 
+       // This creates a clone of depth_shader with base material set to EMT_SOLID,
+       // because entities won't render shadows with base material EMP_ONETEXTURE_BLEND
+       if (depth_shader_entities == -1) {
+               std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
+               if (depth_shader_vs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping vs shader not found." << std::endl;
+                       return;
+               }
+               std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
+               if (depth_shader_fs.empty()) {
+                       m_shadows_enabled = false;
+                       errorstream << "Error shadow mapping fs shader not found." << std::endl;
+                       return;
+               }
+
+               depth_shader_entities = gpu->addHighLevelShaderMaterial(
+                               readShaderFile(depth_shader_vs).c_str(), "vertexMain",
+                               video::EVST_VS_1_1,
+                               readShaderFile(depth_shader_fs).c_str(), "pixelMain",
+                               video::EPST_PS_1_2, m_shadow_depth_cb);
+
+               if (depth_shader_entities == -1) {
+                       // upsi, something went wrong loading shader.
+                       m_shadows_enabled = false;
+                       errorstream << "Error compiling shadow mapping shader (dynamic)." << std::endl;
+                       return;
+               }
+
+               // HACK, TODO: investigate this better
+               // Grab the material renderer once more so minetest doesn't crash
+               // on exit
+               m_driver->getMaterialRenderer(depth_shader_entities)->grab();
+       }
+
        if (mixcsm_shader == -1) {
                std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
                if (depth_shader_vs.empty()) {
index e633bd4f7029dbd775a88c283b5d0e5fc81d8802..52b24a18f16c7b43d5ac50d9c000caee89404776 100644 (file)
@@ -64,7 +64,6 @@ class ShadowRenderer
        size_t getDirectionalLightCount() const;
        f32 getMaxShadowFar() const;
 
-       float getUpdateDelta() const;
        /// Adds a shadow to the scene node.
        /// The shadow mode can be ESM_BOTH, or ESM_RECEIVE.
        /// ESM_BOTH casts and receives shadows
@@ -101,6 +100,7 @@ class ShadowRenderer
                                        scene::ESNRP_SOLID);
        void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
        void mixShadowsQuad();
+       void updateSMTextures();
 
        // a bunch of variables
        IrrlichtDevice *m_device{nullptr};
@@ -108,6 +108,7 @@ class ShadowRenderer
        video::IVideoDriver *m_driver{nullptr};
        Client *m_client{nullptr};
        video::ITexture *shadowMapClientMap{nullptr};
+       video::ITexture *shadowMapClientMapFuture{nullptr};
        video::ITexture *shadowMapTextureFinal{nullptr};
        video::ITexture *shadowMapTextureDynamicObjects{nullptr};
        video::ITexture *shadowMapTextureColors{nullptr};
@@ -120,11 +121,12 @@ class ShadowRenderer
        float m_shadow_map_max_distance;
        float m_shadow_map_texture_size;
        float m_time_day{0.0f};
-       float m_update_delta;
        int m_shadow_samples;
        bool m_shadow_map_texture_32bit;
        bool m_shadows_enabled;
        bool m_shadow_map_colored;
+       u8 m_map_shadow_update_frames; /* Use this number of frames to update map shaodw */
+       u8 m_current_frame{0}; /* Current frame */
 
        video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F};
        video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
@@ -135,6 +137,7 @@ class ShadowRenderer
        std::string readShaderFile(const std::string &path);
 
        s32 depth_shader{-1};
+       s32 depth_shader_entities{-1};
        s32 depth_shader_trans{-1};
        s32 mixcsm_shader{-1};
 
index 6791fccf567e3a228684f761009e7fc95934a2b6..faf839b3acb78412ad11c1c6757ea90600bf488f 100644 (file)
@@ -272,7 +272,7 @@ void set_default_settings()
        settings->setDefault("shadow_map_color", "false");
        settings->setDefault("shadow_filters", "1");
        settings->setDefault("shadow_poisson_filter", "true");
-       settings->setDefault("shadow_update_time", "0.2");
+       settings->setDefault("shadow_update_frames", "8");
        settings->setDefault("shadow_soft_radius", "1.0");
        settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");