forked from Mirrorlandia_minetest/minetest
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>
This commit is contained in:
parent
ff2d2a6e93
commit
bf3acbf388
@ -618,11 +618,11 @@ shadow_filters (Shadow filter quality) enum 1 0,1,2
|
|||||||
# On true translucent nodes cast colored shadows. This is expensive.
|
# On true translucent nodes cast colored shadows. This is expensive.
|
||||||
shadow_map_color (Colored shadows) bool false
|
shadow_map_color (Colored shadows) bool false
|
||||||
|
|
||||||
|
# Spread a complete update of shadow map over given amount of frames.
|
||||||
# Set the shadow update time, in seconds.
|
# Higher values might make shadows laggy, lower values
|
||||||
# Lower value means shadows and map updates faster, but it consumes more resources.
|
# will consume more resources.
|
||||||
# Minimum value: 0.001; maximum value: 0.2
|
# Minimum value: 1; maximum value: 16
|
||||||
shadow_update_time (Map update time) float 0.2 0.001 0.2
|
shadow_update_frames (Map shadows update frames) int 8 1 16
|
||||||
|
|
||||||
# Set the soft shadow radius size.
|
# Set the soft shadow radius size.
|
||||||
# Lower values mean sharper shadows, bigger values mean softer shadows.
|
# Lower values mean sharper shadows, bigger values mean softer shadows.
|
||||||
|
@ -197,7 +197,7 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist
|
|||||||
float pointDepth;
|
float pointDepth;
|
||||||
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
|
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;
|
int n = 0;
|
||||||
|
|
||||||
for (y = -bound; y <= bound; y += 1.0)
|
for (y = -bound; y <= bound; y += 1.0)
|
||||||
@ -304,7 +304,7 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
|
|||||||
float perspectiveFactor;
|
float perspectiveFactor;
|
||||||
|
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
|
||||||
int end_offset = int(samples) + init_offset;
|
int end_offset = int(samples) + init_offset;
|
||||||
|
|
||||||
@ -334,7 +334,7 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
|
|||||||
float perspectiveFactor;
|
float perspectiveFactor;
|
||||||
|
|
||||||
float texture_size = 1.0 / (f_textureresolution * 0.5);
|
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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
|
||||||
int end_offset = int(samples) + init_offset;
|
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 texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
float y, x;
|
float y, x;
|
||||||
float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
|
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
// basic PCF filter
|
// 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 texture_size = 1.0 / (f_textureresolution * 0.5);
|
||||||
float y, x;
|
float y, x;
|
||||||
float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
|
float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
// basic PCF filter
|
// basic PCF filter
|
||||||
|
@ -636,7 +636,7 @@ void ClientMap::PrintInfo(std::ostream &out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
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;
|
bool is_transparent_pass = pass != scene::ESNRP_SOLID;
|
||||||
std::string prefix;
|
std::string prefix;
|
||||||
@ -650,7 +650,23 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
|
|
||||||
MeshBufListList drawbufs;
|
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) {
|
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;
|
v3s16 block_pos = i.first;
|
||||||
MapBlock *block = i.second;
|
MapBlock *block = i.second;
|
||||||
|
|
||||||
@ -705,6 +721,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
local_material.MaterialType = material.MaterialType;
|
local_material.MaterialType = material.MaterialType;
|
||||||
local_material.BackfaceCulling = material.BackfaceCulling;
|
local_material.BackfaceCulling = material.BackfaceCulling;
|
||||||
local_material.FrontfaceCulling = material.FrontfaceCulling;
|
local_material.FrontfaceCulling = material.FrontfaceCulling;
|
||||||
|
local_material.BlendOperation = material.BlendOperation;
|
||||||
local_material.Lighting = false;
|
local_material.Lighting = false;
|
||||||
driver->setMaterial(local_material);
|
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 + "draw meshes [ms]", draw.stop(true));
|
||||||
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
|
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
|
||||||
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
|
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
|
||||||
|
@ -125,7 +125,7 @@ public:
|
|||||||
void renderMap(video::IVideoDriver* driver, s32 pass);
|
void renderMap(video::IVideoDriver* driver, s32 pass);
|
||||||
|
|
||||||
void renderMapShadows(video::IVideoDriver *driver,
|
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 getBackgroundBrightness(float max_d, u32 daylight_factor,
|
||||||
int oldvalue, bool *sunlight_seen_result);
|
int oldvalue, bool *sunlight_seen_result);
|
||||||
|
@ -609,7 +609,6 @@ struct GameRunData {
|
|||||||
float jump_timer;
|
float jump_timer;
|
||||||
float damage_flash;
|
float damage_flash;
|
||||||
float update_draw_list_timer;
|
float update_draw_list_timer;
|
||||||
float update_shadows_timer;
|
|
||||||
|
|
||||||
f32 fog_range;
|
f32 fog_range;
|
||||||
|
|
||||||
@ -3881,10 +3880,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
|||||||
changed much
|
changed much
|
||||||
*/
|
*/
|
||||||
runData.update_draw_list_timer += dtime;
|
runData.update_draw_list_timer += dtime;
|
||||||
runData.update_shadows_timer += dtime;
|
|
||||||
|
|
||||||
float update_draw_list_delta = 0.2f;
|
float update_draw_list_delta = 0.2f;
|
||||||
bool draw_list_updated = false;
|
|
||||||
|
|
||||||
v3f camera_direction = camera->getDirection();
|
v3f camera_direction = camera->getDirection();
|
||||||
if (runData.update_draw_list_timer >= update_draw_list_delta
|
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;
|
runData.update_draw_list_timer = 0;
|
||||||
client->getEnv().getClientMap().updateDrawList();
|
client->getEnv().getClientMap().updateDrawList();
|
||||||
runData.update_draw_list_last_cam_dir = camera_direction;
|
runData.update_draw_list_last_cam_dir = camera_direction;
|
||||||
draw_list_updated = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
|
if (RenderingEngine::get_shadow_renderer()) {
|
||||||
update_draw_list_delta = shadow->getUpdateDelta();
|
updateShadows();
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
|
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->getDirectionalLight().setDirection(sun_pos);
|
||||||
shadow->setTimeOfDay(in_timeofday);
|
shadow->setTimeOfDay(in_timeofday);
|
||||||
|
|
||||||
shadow->getDirectionalLight().update_frustum(camera, client);
|
shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
@ -38,8 +38,8 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
|
|||||||
float tanFovX = tanf(cam->getFovX() * 0.5f);
|
float tanFovX = tanf(cam->getFovX() * 0.5f);
|
||||||
|
|
||||||
// adjusted frustum boundaries
|
// adjusted frustum boundaries
|
||||||
float sfNear = shadow_frustum.zNear;
|
float sfNear = future_frustum.zNear;
|
||||||
float sfFar = adjustDist(shadow_frustum.zFar, cam->getFovY());
|
float sfFar = adjustDist(future_frustum.zFar, cam->getFovY());
|
||||||
|
|
||||||
// adjusted camera positions
|
// adjusted camera positions
|
||||||
v3f camPos2 = cam->getPosition();
|
v3f camPos2 = cam->getPosition();
|
||||||
@ -87,14 +87,15 @@ void DirectionalLight::createSplitMatrices(const Camera *cam)
|
|||||||
v3f eye_displacement = direction * vvolume;
|
v3f eye_displacement = direction * vvolume;
|
||||||
|
|
||||||
// we must compute the viewmat with the position - the camera offset
|
// 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;
|
v3f eye = frustumCenter - eye_displacement;
|
||||||
shadow_frustum.position = world_center - eye_displacement;
|
future_frustum.position = world_center - eye_displacement;
|
||||||
shadow_frustum.length = vvolume;
|
future_frustum.length = vvolume;
|
||||||
shadow_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
|
future_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f));
|
||||||
shadow_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(shadow_frustum.length,
|
future_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(future_frustum.length,
|
||||||
shadow_frustum.length, -shadow_frustum.length,
|
future_frustum.length, -future_frustum.length,
|
||||||
shadow_frustum.length,false);
|
future_frustum.length,false);
|
||||||
|
future_frustum.camera_offset = cam->getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
|
DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
|
||||||
@ -104,23 +105,44 @@ DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
|
|||||||
farPlane(farValue), mapRes(shadowMapResolution), pos(position)
|
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 zNear = cam->getCameraNode()->getNearValue();
|
||||||
float zFar = getMaxFarValue();
|
float zFar = getMaxFarValue();
|
||||||
|
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
// update splits near and fars
|
// update splits near and fars
|
||||||
shadow_frustum.zNear = zNear;
|
future_frustum.zNear = zNear;
|
||||||
shadow_frustum.zFar = zFar;
|
future_frustum.zFar = zFar;
|
||||||
|
|
||||||
// update shadow frustum
|
// update shadow frustum
|
||||||
createSplitMatrices(cam);
|
createSplitMatrices(cam);
|
||||||
// get the draw list for shadows
|
// get the draw list for shadows
|
||||||
client->getEnv().getClientMap().updateDrawListShadow(
|
client->getEnv().getClientMap().updateDrawListShadow(
|
||||||
getPosition(), getDirection(), shadow_frustum.length);
|
getPosition(), getDirection(), future_frustum.length);
|
||||||
should_update_map_shadow = true;
|
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)
|
void DirectionalLight::setDirection(v3f dir)
|
||||||
@ -144,6 +166,16 @@ const m4f &DirectionalLight::getProjectionMatrix() const
|
|||||||
return shadow_frustum.ProjOrthMat;
|
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()
|
m4f DirectionalLight::getViewProjMatrix()
|
||||||
{
|
{
|
||||||
return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
|
return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
|
||||||
|
@ -34,6 +34,7 @@ struct shadowFrustum
|
|||||||
core::matrix4 ProjOrthMat;
|
core::matrix4 ProjOrthMat;
|
||||||
core::matrix4 ViewMat;
|
core::matrix4 ViewMat;
|
||||||
v3f position;
|
v3f position;
|
||||||
|
v3s16 camera_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirectionalLight
|
class DirectionalLight
|
||||||
@ -47,7 +48,7 @@ public:
|
|||||||
|
|
||||||
//DISABLE_CLASS_COPY(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)
|
// when set direction is updated to negative normalized(direction)
|
||||||
void setDirection(v3f dir);
|
void setDirection(v3f dir);
|
||||||
@ -59,6 +60,8 @@ public:
|
|||||||
/// Gets the light's matrices.
|
/// Gets the light's matrices.
|
||||||
const core::matrix4 &getViewMatrix() const;
|
const core::matrix4 &getViewMatrix() const;
|
||||||
const core::matrix4 &getProjectionMatrix() const;
|
const core::matrix4 &getProjectionMatrix() const;
|
||||||
|
const core::matrix4 &getFutureViewMatrix() const;
|
||||||
|
const core::matrix4 &getFutureProjectionMatrix() const;
|
||||||
core::matrix4 getViewProjMatrix();
|
core::matrix4 getViewProjMatrix();
|
||||||
|
|
||||||
/// Gets the light's far value.
|
/// Gets the light's far value.
|
||||||
@ -88,6 +91,8 @@ public:
|
|||||||
|
|
||||||
bool should_update_map_shadow{true};
|
bool should_update_map_shadow{true};
|
||||||
|
|
||||||
|
void commitFrustum();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createSplitMatrices(const Camera *cam);
|
void createSplitMatrices(const Camera *cam);
|
||||||
|
|
||||||
@ -99,4 +104,6 @@ private:
|
|||||||
v3f pos;
|
v3f pos;
|
||||||
v3f direction{0};
|
v3f direction{0};
|
||||||
shadowFrustum shadow_frustum;
|
shadowFrustum shadow_frustum;
|
||||||
|
shadowFrustum future_frustum;
|
||||||
|
bool dirty{false};
|
||||||
};
|
};
|
||||||
|
@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "client/shader.h"
|
#include "client/shader.h"
|
||||||
#include "client/client.h"
|
#include "client/client.h"
|
||||||
#include "client/clientmap.h"
|
#include "client/clientmap.h"
|
||||||
|
#include "profiler.h"
|
||||||
|
|
||||||
ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
|
ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
|
||||||
m_device(device), m_smgr(device->getSceneManager()),
|
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;
|
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_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
|
||||||
m_shadow_map_colored = g_settings->getBool("shadow_map_color");
|
m_shadow_map_colored = g_settings->getBool("shadow_map_color");
|
||||||
m_shadow_samples = g_settings->getS32("shadow_filters");
|
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()
|
ShadowRenderer::~ShadowRenderer()
|
||||||
@ -66,6 +67,9 @@ ShadowRenderer::~ShadowRenderer()
|
|||||||
|
|
||||||
if (shadowMapClientMap)
|
if (shadowMapClientMap)
|
||||||
m_driver->removeTexture(shadowMapClientMap);
|
m_driver->removeTexture(shadowMapClientMap);
|
||||||
|
|
||||||
|
if (shadowMapClientMapFuture)
|
||||||
|
m_driver->removeTexture(shadowMapClientMapFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShadowRenderer::initialize()
|
void ShadowRenderer::initialize()
|
||||||
@ -93,11 +97,6 @@ void ShadowRenderer::initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float ShadowRenderer::getUpdateDelta() const
|
|
||||||
{
|
|
||||||
return m_update_delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ShadowRenderer::addDirectionalLight()
|
size_t ShadowRenderer::addDirectionalLight()
|
||||||
{
|
{
|
||||||
m_light_list.emplace_back(m_shadow_map_texture_size,
|
m_light_list.emplace_back(m_shadow_map_texture_size,
|
||||||
@ -152,10 +151,9 @@ void ShadowRenderer::setClearColor(video::SColor ClearColor)
|
|||||||
m_clear_color = ClearColor;
|
m_clear_color = ClearColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShadowRenderer::update(video::ITexture *outputTarget)
|
void ShadowRenderer::updateSMTextures()
|
||||||
{
|
{
|
||||||
if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
|
if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
|
||||||
m_smgr->drawAll();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +172,13 @@ void ShadowRenderer::update(video::ITexture *outputTarget)
|
|||||||
true);
|
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) {
|
if (m_shadow_map_colored && !shadowMapTextureColors) {
|
||||||
shadowMapTextureColors = getSMTexture(
|
shadowMapTextureColors = getSMTexture(
|
||||||
std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
|
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()) {
|
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) {
|
for (DirectionalLight &light : m_light_list) {
|
||||||
// Static shader values.
|
// Static shader values.
|
||||||
m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
|
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
|
// Depth texture is available in irrlicth maybe we
|
||||||
// should put some gl* fn here
|
// 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));
|
video::SColor(255, 255, 255, 255));
|
||||||
renderShadowMap(shadowMapClientMap, light);
|
renderShadowMap(shadowMapTargetTexture, light);
|
||||||
|
|
||||||
if (m_shadow_map_colored) {
|
// Render transparent part in one pass.
|
||||||
m_driver->setRenderTarget(shadowMapTextureColors,
|
// This is also handled in ClientMap.
|
||||||
true, false, video::SColor(255, 255, 255, 255));
|
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);
|
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.
|
// render shadows for the n0n-map objects.
|
||||||
m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
|
m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
|
||||||
true, video::SColor(255, 255, 255, 255));
|
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,
|
void ShadowRenderer::renderShadowMap(video::ITexture *target,
|
||||||
DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
|
DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
|
||||||
{
|
{
|
||||||
m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
|
m_driver->setTransform(video::ETS_VIEW, light.getFutureViewMatrix());
|
||||||
m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
|
m_driver->setTransform(video::ETS_PROJECTION, light.getFutureProjectionMatrix());
|
||||||
|
|
||||||
// Operate on the client map
|
// Operate on the client map
|
||||||
for (const auto &shadow_node : m_shadow_node_array) {
|
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.PolygonOffsetDepthBias = 1.0f/4.0f;
|
||||||
//material.PolygonOffsetSlopeScale = -1.f;
|
//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;
|
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
|
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
|
||||||
|
material.BlendOperation = video::EBO_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: I don't think this is needed here
|
// FIXME: I don't think this is needed here
|
||||||
map_node->OnAnimate(m_device->getTimer()->getTime());
|
map_node->OnAnimate(m_device->getTimer()->getTime());
|
||||||
@ -333,7 +394,7 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target,
|
|||||||
m_driver->setTransform(video::ETS_WORLD,
|
m_driver->setTransform(video::ETS_WORLD,
|
||||||
map_node->getAbsoluteTransformation());
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,8 +415,10 @@ void ShadowRenderer::renderShadowObjects(
|
|||||||
u32 n_node_materials = shadow_node.node->getMaterialCount();
|
u32 n_node_materials = shadow_node.node->getMaterialCount();
|
||||||
std::vector<s32> BufferMaterialList;
|
std::vector<s32> BufferMaterialList;
|
||||||
std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
|
std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
|
||||||
|
std::vector<video::E_BLEND_OPERATION> BufferBlendOperationList;
|
||||||
BufferMaterialList.reserve(n_node_materials);
|
BufferMaterialList.reserve(n_node_materials);
|
||||||
BufferMaterialCullingList.reserve(n_node_materials);
|
BufferMaterialCullingList.reserve(n_node_materials);
|
||||||
|
BufferBlendOperationList.reserve(n_node_materials);
|
||||||
|
|
||||||
// backup materialtype for each material
|
// backup materialtype for each material
|
||||||
// (aka shader)
|
// (aka shader)
|
||||||
@ -365,12 +428,11 @@ void ShadowRenderer::renderShadowObjects(
|
|||||||
|
|
||||||
BufferMaterialList.push_back(current_mat.MaterialType);
|
BufferMaterialList.push_back(current_mat.MaterialType);
|
||||||
current_mat.MaterialType =
|
current_mat.MaterialType =
|
||||||
(video::E_MATERIAL_TYPE)depth_shader;
|
(video::E_MATERIAL_TYPE)depth_shader_entities;
|
||||||
|
|
||||||
current_mat.setTexture(3, shadowMapTextureFinal);
|
|
||||||
|
|
||||||
BufferMaterialCullingList.emplace_back(
|
BufferMaterialCullingList.emplace_back(
|
||||||
(bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
|
(bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
|
||||||
|
BufferBlendOperationList.push_back(current_mat.BlendOperation);
|
||||||
|
|
||||||
current_mat.BackfaceCulling = true;
|
current_mat.BackfaceCulling = true;
|
||||||
current_mat.FrontfaceCulling = false;
|
current_mat.FrontfaceCulling = false;
|
||||||
@ -393,6 +455,7 @@ void ShadowRenderer::renderShadowObjects(
|
|||||||
|
|
||||||
current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
|
current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
|
||||||
current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
|
current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
|
||||||
|
current_mat.BlendOperation = BufferBlendOperationList[m];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end for caster shadow nodes
|
} // end for caster shadow nodes
|
||||||
@ -433,7 +496,7 @@ void ShadowRenderer::createShaders()
|
|||||||
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
|
readShaderFile(depth_shader_vs).c_str(), "vertexMain",
|
||||||
video::EVST_VS_1_1,
|
video::EVST_VS_1_1,
|
||||||
readShaderFile(depth_shader_fs).c_str(), "pixelMain",
|
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) {
|
if (depth_shader == -1) {
|
||||||
// upsi, something went wrong loading shader.
|
// upsi, something went wrong loading shader.
|
||||||
@ -449,6 +512,41 @@ void ShadowRenderer::createShaders()
|
|||||||
m_driver->getMaterialRenderer(depth_shader)->grab();
|
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) {
|
if (mixcsm_shader == -1) {
|
||||||
std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
|
std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
|
||||||
if (depth_shader_vs.empty()) {
|
if (depth_shader_vs.empty()) {
|
||||||
|
@ -64,7 +64,6 @@ public:
|
|||||||
size_t getDirectionalLightCount() const;
|
size_t getDirectionalLightCount() const;
|
||||||
f32 getMaxShadowFar() const;
|
f32 getMaxShadowFar() const;
|
||||||
|
|
||||||
float getUpdateDelta() const;
|
|
||||||
/// Adds a shadow to the scene node.
|
/// Adds a shadow to the scene node.
|
||||||
/// The shadow mode can be ESM_BOTH, or ESM_RECEIVE.
|
/// The shadow mode can be ESM_BOTH, or ESM_RECEIVE.
|
||||||
/// ESM_BOTH casts and receives shadows
|
/// ESM_BOTH casts and receives shadows
|
||||||
@ -101,6 +100,7 @@ private:
|
|||||||
scene::ESNRP_SOLID);
|
scene::ESNRP_SOLID);
|
||||||
void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
|
void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
|
||||||
void mixShadowsQuad();
|
void mixShadowsQuad();
|
||||||
|
void updateSMTextures();
|
||||||
|
|
||||||
// a bunch of variables
|
// a bunch of variables
|
||||||
IrrlichtDevice *m_device{nullptr};
|
IrrlichtDevice *m_device{nullptr};
|
||||||
@ -108,6 +108,7 @@ private:
|
|||||||
video::IVideoDriver *m_driver{nullptr};
|
video::IVideoDriver *m_driver{nullptr};
|
||||||
Client *m_client{nullptr};
|
Client *m_client{nullptr};
|
||||||
video::ITexture *shadowMapClientMap{nullptr};
|
video::ITexture *shadowMapClientMap{nullptr};
|
||||||
|
video::ITexture *shadowMapClientMapFuture{nullptr};
|
||||||
video::ITexture *shadowMapTextureFinal{nullptr};
|
video::ITexture *shadowMapTextureFinal{nullptr};
|
||||||
video::ITexture *shadowMapTextureDynamicObjects{nullptr};
|
video::ITexture *shadowMapTextureDynamicObjects{nullptr};
|
||||||
video::ITexture *shadowMapTextureColors{nullptr};
|
video::ITexture *shadowMapTextureColors{nullptr};
|
||||||
@ -120,11 +121,12 @@ private:
|
|||||||
float m_shadow_map_max_distance;
|
float m_shadow_map_max_distance;
|
||||||
float m_shadow_map_texture_size;
|
float m_shadow_map_texture_size;
|
||||||
float m_time_day{0.0f};
|
float m_time_day{0.0f};
|
||||||
float m_update_delta;
|
|
||||||
int m_shadow_samples;
|
int m_shadow_samples;
|
||||||
bool m_shadow_map_texture_32bit;
|
bool m_shadow_map_texture_32bit;
|
||||||
bool m_shadows_enabled;
|
bool m_shadows_enabled;
|
||||||
bool m_shadow_map_colored;
|
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{video::ECOLOR_FORMAT::ECF_R16F};
|
||||||
video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
|
video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
|
||||||
@ -135,6 +137,7 @@ private:
|
|||||||
std::string readShaderFile(const std::string &path);
|
std::string readShaderFile(const std::string &path);
|
||||||
|
|
||||||
s32 depth_shader{-1};
|
s32 depth_shader{-1};
|
||||||
|
s32 depth_shader_entities{-1};
|
||||||
s32 depth_shader_trans{-1};
|
s32 depth_shader_trans{-1};
|
||||||
s32 mixcsm_shader{-1};
|
s32 mixcsm_shader{-1};
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ void set_default_settings()
|
|||||||
settings->setDefault("shadow_map_color", "false");
|
settings->setDefault("shadow_map_color", "false");
|
||||||
settings->setDefault("shadow_filters", "1");
|
settings->setDefault("shadow_filters", "1");
|
||||||
settings->setDefault("shadow_poisson_filter", "true");
|
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_soft_radius", "1.0");
|
||||||
settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
|
settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user