aboutsummaryrefslogtreecommitdiff
path: root/src/client/shadows
diff options
context:
space:
mode:
authorx2048 <codeforsmile@gmail.com>2021-07-25 12:36:23 +0200
committerGitHub <noreply@github.com>2021-07-25 12:36:23 +0200
commitbf3acbf388406f736286d990adb5f35a9023c390 (patch)
tree9fdff755e37253580c222ff768802a6b0170be10 /src/client/shadows
parentff2d2a6e93d75d24b3f69f2b3690bcac6440961e (diff)
downloadminetest-bf3acbf388406f736286d990adb5f35a9023c390.tar.gz
minetest-bf3acbf388406f736286d990adb5f35a9023c390.tar.bz2
minetest-bf3acbf388406f736286d990adb5f35a9023c390.zip
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 <sfan5@live.de>
Diffstat (limited to 'src/client/shadows')
-rw-r--r--src/client/shadows/dynamicshadows.cpp60
-rw-r--r--src/client/shadows/dynamicshadows.h9
-rw-r--r--src/client/shadows/dynamicshadowsrender.cpp156
-rw-r--r--src/client/shadows/dynamicshadowsrender.h7
4 files changed, 186 insertions, 46 deletions
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;
diff --git a/src/client/shadows/dynamicshadows.h b/src/client/shadows/dynamicshadows.h
index a53612f7c..d8be66be8 100644
--- a/src/client/shadows/dynamicshadows.h
+++ b/src/client/shadows/dynamicshadows.h
@@ -34,6 +34,7 @@ struct shadowFrustum
core::matrix4 ProjOrthMat;
core::matrix4 ViewMat;
v3f position;
+ v3s16 camera_offset;
};
class DirectionalLight
@@ -47,7 +48,7 @@ public:
//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 @@ public:
/// 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 @@ public:
bool should_update_map_shadow{true};
+ void commitFrustum();
+
private:
void createSplitMatrices(const Camera *cam);
@@ -99,4 +104,6 @@ private:
v3f pos;
v3f direction{0};
shadowFrustum shadow_frustum;
+ shadowFrustum future_frustum;
+ bool dirty{false};
};
diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp
index 135c9f895..350586225 100644
--- a/src/client/shadows/dynamicshadowsrender.cpp
+++ b/src/client/shadows/dynamicshadowsrender.cpp
@@ -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()) {
diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h
index e633bd4f7..52b24a18f 100644
--- a/src/client/shadows/dynamicshadowsrender.h
+++ b/src/client/shadows/dynamicshadowsrender.h
@@ -64,7 +64,6 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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};