From bf3acbf388406f736286d990adb5f35a9023c390 Mon Sep 17 00:00:00 2001 From: x2048 Date: Sun, 25 Jul 2021 12:36:23 +0200 Subject: Distribute shadow map update over multiple frames to reduce stutter (#11422) 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 --- src/client/shadows/dynamicshadows.cpp | 60 +++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 14 deletions(-) (limited to 'src/client/shadows/dynamicshadows.cpp') diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index 17b711a61..0c7eea0e7 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -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; -- cgit v1.2.3