3 Copyright (C) 2021 Liso <anlismon@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "client/shadows/dynamicshadowsrender.h"
23 #include "client/shadows/shadowsScreenQuad.h"
24 #include "client/shadows/shadowsshadercallbacks.h"
27 #include "util/string.h"
28 #include "client/shader.h"
29 #include "client/client.h"
30 #include "client/clientmap.h"
33 ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
34 m_smgr(device->getSceneManager()), m_driver(device->getVideoDriver()),
35 m_client(client), m_current_frame(0),
36 m_perspective_bias_xy(0.8), m_perspective_bias_z(0.5)
40 m_shadows_supported = true; // assume shadows supported. We will check actual support in initialize
41 m_shadows_enabled = true;
43 m_shadow_strength_gamma = g_settings->getFloat("shadow_strength_gamma");
44 if (std::isnan(m_shadow_strength_gamma))
45 m_shadow_strength_gamma = 1.0f;
46 m_shadow_strength_gamma = core::clamp(m_shadow_strength_gamma, 0.1f, 10.0f);
48 m_shadow_map_max_distance = g_settings->getFloat("shadow_map_max_distance");
50 m_shadow_map_texture_size = g_settings->getFloat("shadow_map_texture_size");
52 m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
53 m_shadow_map_colored = g_settings->getBool("shadow_map_color");
54 m_shadow_samples = g_settings->getS32("shadow_filters");
55 m_map_shadow_update_frames = g_settings->getS16("shadow_update_frames");
58 ShadowRenderer::~ShadowRenderer()
60 // call to disable releases dynamically allocated resources
63 if (m_shadow_depth_cb)
64 delete m_shadow_depth_cb;
65 if (m_shadow_depth_entity_cb)
66 delete m_shadow_depth_entity_cb;
67 if (m_shadow_depth_trans_cb)
68 delete m_shadow_depth_trans_cb;
70 delete m_shadow_mix_cb;
71 m_shadow_node_array.clear();
74 if (shadowMapTextureDynamicObjects)
75 m_driver->removeTexture(shadowMapTextureDynamicObjects);
77 if (shadowMapTextureFinal)
78 m_driver->removeTexture(shadowMapTextureFinal);
80 if (shadowMapTextureColors)
81 m_driver->removeTexture(shadowMapTextureColors);
83 if (shadowMapClientMap)
84 m_driver->removeTexture(shadowMapClientMap);
86 if (shadowMapClientMapFuture)
87 m_driver->removeTexture(shadowMapClientMapFuture);
90 void ShadowRenderer::disable()
92 m_shadows_enabled = false;
93 if (shadowMapTextureFinal) {
94 m_driver->setRenderTarget(shadowMapTextureFinal, true, true,
95 video::SColor(255, 255, 255, 255));
96 m_driver->setRenderTarget(0, true, true);
100 void ShadowRenderer::initialize()
102 auto *gpu = m_driver->getGPUProgrammingServices();
105 if (m_shadows_supported && gpu && m_driver->queryFeature(video::EVDF_ARB_GLSL)) {
108 m_shadows_supported = false;
110 warningstream << "Shadows: GLSL Shader not supported on this system."
115 m_texture_format = m_shadow_map_texture_32bit
116 ? video::ECOLOR_FORMAT::ECF_R32F
117 : video::ECOLOR_FORMAT::ECF_R16F;
119 m_texture_format_color = m_shadow_map_texture_32bit
120 ? video::ECOLOR_FORMAT::ECF_G32R32F
121 : video::ECOLOR_FORMAT::ECF_G16R16F;
123 m_shadows_enabled &= m_shadows_supported;
127 size_t ShadowRenderer::addDirectionalLight()
129 m_light_list.emplace_back(m_shadow_map_texture_size,
131 video::SColor(255, 255, 255, 255), m_shadow_map_max_distance);
132 return m_light_list.size() - 1;
135 DirectionalLight &ShadowRenderer::getDirectionalLight(u32 index)
137 return m_light_list[index];
140 size_t ShadowRenderer::getDirectionalLightCount() const
142 return m_light_list.size();
145 f32 ShadowRenderer::getMaxShadowFar() const
147 if (!m_light_list.empty()) {
148 float zMax = m_light_list[0].getFarValue();
154 void ShadowRenderer::setShadowIntensity(float shadow_intensity)
156 m_shadow_strength = pow(shadow_intensity, 1.0f / m_shadow_strength_gamma);
157 if (m_shadow_strength > 1E-2)
163 void ShadowRenderer::addNodeToShadowList(
164 scene::ISceneNode *node, E_SHADOW_MODE shadowMode)
166 m_shadow_node_array.emplace_back(NodeToApply(node, shadowMode));
169 void ShadowRenderer::removeNodeFromShadowList(scene::ISceneNode *node)
171 for (auto it = m_shadow_node_array.begin(); it != m_shadow_node_array.end();) {
172 if (it->node == node) {
173 it = m_shadow_node_array.erase(it);
181 void ShadowRenderer::updateSMTextures()
183 if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
187 if (!shadowMapTextureDynamicObjects) {
189 shadowMapTextureDynamicObjects = getSMTexture(
190 std::string("shadow_dynamic_") + itos(m_shadow_map_texture_size),
191 m_texture_format, true);
192 assert(shadowMapTextureDynamicObjects != nullptr);
195 if (!shadowMapClientMap) {
197 shadowMapClientMap = getSMTexture(
198 std::string("shadow_clientmap_") + itos(m_shadow_map_texture_size),
199 m_shadow_map_colored ? m_texture_format_color : m_texture_format,
201 assert(shadowMapClientMap != nullptr);
204 if (!shadowMapClientMapFuture && m_map_shadow_update_frames > 1) {
205 shadowMapClientMapFuture = getSMTexture(
206 std::string("shadow_clientmap_bb_") + itos(m_shadow_map_texture_size),
207 m_shadow_map_colored ? m_texture_format_color : m_texture_format,
209 assert(shadowMapClientMapFuture != nullptr);
212 if (m_shadow_map_colored && !shadowMapTextureColors) {
213 shadowMapTextureColors = getSMTexture(
214 std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
215 m_shadow_map_colored ? m_texture_format_color : m_texture_format,
217 assert(shadowMapTextureColors != nullptr);
220 // The merge all shadowmaps texture
221 if (!shadowMapTextureFinal) {
222 video::ECOLOR_FORMAT frt;
223 if (m_shadow_map_texture_32bit) {
224 if (m_shadow_map_colored)
225 frt = video::ECOLOR_FORMAT::ECF_A32B32G32R32F;
227 frt = video::ECOLOR_FORMAT::ECF_R32F;
229 if (m_shadow_map_colored)
230 frt = video::ECOLOR_FORMAT::ECF_A16B16G16R16F;
232 frt = video::ECOLOR_FORMAT::ECF_R16F;
234 shadowMapTextureFinal = getSMTexture(
235 std::string("shadowmap_final_") + itos(m_shadow_map_texture_size),
237 assert(shadowMapTextureFinal != nullptr);
240 if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
241 bool reset_sm_texture = false;
243 // detect if SM should be regenerated
244 for (DirectionalLight &light : m_light_list) {
245 if (light.should_update_map_shadow || m_force_update_shadow_map) {
246 light.should_update_map_shadow = false;
248 reset_sm_texture = true;
252 video::ITexture* shadowMapTargetTexture = shadowMapClientMapFuture;
253 if (shadowMapTargetTexture == nullptr)
254 shadowMapTargetTexture = shadowMapClientMap;
256 // Update SM incrementally:
257 for (DirectionalLight &light : m_light_list) {
258 // Static shader values.
259 for (auto cb : {m_shadow_depth_cb, m_shadow_depth_entity_cb, m_shadow_depth_trans_cb})
261 cb->MapRes = (f32)m_shadow_map_texture_size;
262 cb->MaxFar = (f32)m_shadow_map_max_distance * BS;
263 cb->PerspectiveBiasXY = getPerspectiveBiasXY();
264 cb->PerspectiveBiasZ = getPerspectiveBiasZ();
265 cb->CameraPos = light.getFuturePlayerPos();
268 // set the Render Target
269 // right now we can only render in usual RTT, not
270 // Depth texture is available in irrlicth maybe we
271 // should put some gl* fn here
274 if (m_current_frame < m_map_shadow_update_frames || m_force_update_shadow_map) {
275 m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true,
276 video::SColor(255, 255, 255, 255));
277 renderShadowMap(shadowMapTargetTexture, light);
279 // Render transparent part in one pass.
280 // This is also handled in ClientMap.
281 if (m_current_frame == m_map_shadow_update_frames - 1 || m_force_update_shadow_map) {
282 if (m_shadow_map_colored) {
283 m_driver->setRenderTarget(0, false, false);
284 m_driver->setRenderTarget(shadowMapTextureColors,
285 true, false, video::SColor(255, 255, 255, 255));
287 renderShadowMap(shadowMapTextureColors, light,
288 scene::ESNRP_TRANSPARENT);
290 m_driver->setRenderTarget(0, false, false);
293 reset_sm_texture = false;
296 // move to the next section
297 if (m_current_frame <= m_map_shadow_update_frames)
300 // pass finished, swap textures and commit light changes
301 if (m_current_frame == m_map_shadow_update_frames || m_force_update_shadow_map) {
302 if (shadowMapClientMapFuture != nullptr)
303 std::swap(shadowMapClientMapFuture, shadowMapClientMap);
305 // Let all lights know that maps are updated
306 for (DirectionalLight &light : m_light_list)
307 light.commitFrustum();
309 m_force_update_shadow_map = false;
313 void ShadowRenderer::update(video::ITexture *outputTarget)
315 if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
321 if (shadowMapTextureFinal == nullptr) {
325 if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
327 for (DirectionalLight &light : m_light_list) {
328 // Static shader values for entities are set in updateSMTextures
329 // SM texture for entities is not updated incrementally and
330 // must by updated using current player position.
331 m_shadow_depth_entity_cb->CameraPos = light.getPlayerPos();
333 // render shadows for the n0n-map objects.
334 m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
335 true, video::SColor(255, 255, 255, 255));
336 renderShadowObjects(shadowMapTextureDynamicObjects, light);
337 // clear the Render Target
338 m_driver->setRenderTarget(0, false, false);
340 // in order to avoid too many map shadow renders,
341 // we should make a second pass to mix clientmap shadows and
342 // entities shadows :(
343 m_screen_quad->getMaterial().setTexture(0, shadowMapClientMap);
344 // dynamic objs shadow texture.
345 if (m_shadow_map_colored)
346 m_screen_quad->getMaterial().setTexture(1, shadowMapTextureColors);
347 m_screen_quad->getMaterial().setTexture(2, shadowMapTextureDynamicObjects);
349 m_driver->setRenderTarget(shadowMapTextureFinal, false, false,
350 video::SColor(255, 255, 255, 255));
351 m_screen_quad->render(m_driver);
352 m_driver->setRenderTarget(0, false, false);
358 void ShadowRenderer::drawDebug()
360 /* this code just shows shadows textures in screen and in ONLY for debugging*/
362 // this is debug, ignore for now.
363 if (shadowMapTextureFinal)
364 m_driver->draw2DImage(shadowMapTextureFinal,
365 core::rect<s32>(0, 50, 128, 128 + 50),
366 core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
368 if (shadowMapClientMap)
369 m_driver->draw2DImage(shadowMapClientMap,
370 core::rect<s32>(0, 50 + 128, 128, 128 + 50 + 128),
371 core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
373 if (shadowMapTextureDynamicObjects)
374 m_driver->draw2DImage(shadowMapTextureDynamicObjects,
375 core::rect<s32>(0, 128 + 50 + 128, 128,
376 128 + 50 + 128 + 128),
377 core::rect<s32>({0, 0}, shadowMapTextureDynamicObjects->getSize()));
379 if (m_shadow_map_colored && shadowMapTextureColors) {
381 m_driver->draw2DImage(shadowMapTextureColors,
382 core::rect<s32>(128,128 + 50 + 128 + 128,
383 128 + 128, 128 + 50 + 128 + 128 + 128),
384 core::rect<s32>({0, 0}, shadowMapTextureColors->getSize()));
390 video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name,
391 video::ECOLOR_FORMAT texture_format, bool force_creation)
393 if (force_creation) {
394 return m_driver->addRenderTargetTexture(
395 core::dimension2du(m_shadow_map_texture_size,
396 m_shadow_map_texture_size),
397 shadow_map_name.c_str(), texture_format);
400 return m_driver->getTexture(shadow_map_name.c_str());
403 void ShadowRenderer::renderShadowMap(video::ITexture *target,
404 DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
406 m_driver->setTransform(video::ETS_VIEW, light.getFutureViewMatrix());
407 m_driver->setTransform(video::ETS_PROJECTION, light.getFutureProjectionMatrix());
409 // Operate on the client map
410 for (const auto &shadow_node : m_shadow_node_array) {
411 if (strcmp(shadow_node.node->getName(), "ClientMap") != 0)
414 ClientMap *map_node = static_cast<ClientMap *>(shadow_node.node);
416 video::SMaterial material;
417 if (map_node->getMaterialCount() > 0) {
418 // we only want the first material, which is the one with the albedo info
419 material = map_node->getMaterial(0);
422 material.BackfaceCulling = false;
423 material.FrontfaceCulling = true;
425 if (m_shadow_map_colored && pass != scene::ESNRP_SOLID) {
426 material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
429 material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
430 material.BlendOperation = video::EBO_MIN;
433 m_driver->setTransform(video::ETS_WORLD,
434 map_node->getAbsoluteTransformation());
436 int frame = m_force_update_shadow_map ? 0 : m_current_frame;
437 int total_frames = m_force_update_shadow_map ? 1 : m_map_shadow_update_frames;
439 map_node->renderMapShadows(m_driver, material, pass, frame, total_frames);
444 void ShadowRenderer::renderShadowObjects(
445 video::ITexture *target, DirectionalLight &light)
447 m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
448 m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
450 for (const auto &shadow_node : m_shadow_node_array) {
451 // we only take care of the shadow casters
452 if (shadow_node.shadowMode == ESM_RECEIVE ||
453 strcmp(shadow_node.node->getName(), "ClientMap") == 0)
456 // render other objects
457 u32 n_node_materials = shadow_node.node->getMaterialCount();
458 std::vector<s32> BufferMaterialList;
459 std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
460 std::vector<video::E_BLEND_OPERATION> BufferBlendOperationList;
461 BufferMaterialList.reserve(n_node_materials);
462 BufferMaterialCullingList.reserve(n_node_materials);
463 BufferBlendOperationList.reserve(n_node_materials);
465 // backup materialtype for each material
467 // and replace it by our "depth" shader
468 for (u32 m = 0; m < n_node_materials; m++) {
469 auto ¤t_mat = shadow_node.node->getMaterial(m);
471 BufferMaterialList.push_back(current_mat.MaterialType);
472 current_mat.MaterialType =
473 (video::E_MATERIAL_TYPE)depth_shader_entities;
475 BufferMaterialCullingList.emplace_back(
476 (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
477 BufferBlendOperationList.push_back(current_mat.BlendOperation);
479 current_mat.BackfaceCulling = true;
480 current_mat.FrontfaceCulling = false;
483 m_driver->setTransform(video::ETS_WORLD,
484 shadow_node.node->getAbsoluteTransformation());
485 shadow_node.node->render();
487 // restore the material.
489 for (u32 m = 0; m < n_node_materials; m++) {
490 auto ¤t_mat = shadow_node.node->getMaterial(m);
492 current_mat.MaterialType = (video::E_MATERIAL_TYPE) BufferMaterialList[m];
494 current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
495 current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
496 current_mat.BlendOperation = BufferBlendOperationList[m];
499 } // end for caster shadow nodes
502 void ShadowRenderer::mixShadowsQuad()
507 * @Liso's disclaimer ;) This function loads the Shadow Mapping Shaders.
508 * I used a custom loader because I couldn't figure out how to use the base
509 * Shaders system with custom IShaderConstantSetCallBack without messing up the
510 * code too much. If anyone knows how to integrate this with the standard MT
511 * shaders, please feel free to change it.
514 void ShadowRenderer::createShaders()
516 video::IGPUProgrammingServices *gpu = m_driver->getGPUProgrammingServices();
518 if (depth_shader == -1) {
519 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
520 if (depth_shader_vs.empty()) {
521 m_shadows_supported = false;
522 errorstream << "Error shadow mapping vs shader not found." << std::endl;
525 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
526 if (depth_shader_fs.empty()) {
527 m_shadows_supported = false;
528 errorstream << "Error shadow mapping fs shader not found." << std::endl;
531 m_shadow_depth_cb = new ShadowDepthShaderCB();
533 depth_shader = gpu->addHighLevelShaderMaterial(
534 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
536 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
537 video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
539 if (depth_shader == -1) {
540 // upsi, something went wrong loading shader.
541 delete m_shadow_depth_cb;
542 m_shadow_depth_cb = nullptr;
543 m_shadows_enabled = false;
544 m_shadows_supported = false;
545 errorstream << "Error compiling shadow mapping shader." << std::endl;
549 // HACK, TODO: investigate this better
550 // Grab the material renderer once more so minetest doesn't crash
552 m_driver->getMaterialRenderer(depth_shader)->grab();
555 // This creates a clone of depth_shader with base material set to EMT_SOLID,
556 // because entities won't render shadows with base material EMP_ONETEXTURE_BLEND
557 if (depth_shader_entities == -1) {
558 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
559 if (depth_shader_vs.empty()) {
560 m_shadows_supported = false;
561 errorstream << "Error shadow mapping vs shader not found." << std::endl;
564 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
565 if (depth_shader_fs.empty()) {
566 m_shadows_supported = false;
567 errorstream << "Error shadow mapping fs shader not found." << std::endl;
570 m_shadow_depth_entity_cb = new ShadowDepthShaderCB();
572 depth_shader_entities = gpu->addHighLevelShaderMaterial(
573 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
575 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
576 video::EPST_PS_1_2, m_shadow_depth_entity_cb);
578 if (depth_shader_entities == -1) {
579 // upsi, something went wrong loading shader.
580 delete m_shadow_depth_entity_cb;
581 m_shadow_depth_entity_cb = nullptr;
582 m_shadows_enabled = false;
583 m_shadows_supported = false;
584 errorstream << "Error compiling shadow mapping shader (dynamic)." << std::endl;
588 // HACK, TODO: investigate this better
589 // Grab the material renderer once more so minetest doesn't crash
591 m_driver->getMaterialRenderer(depth_shader_entities)->grab();
594 if (mixcsm_shader == -1) {
595 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
596 if (depth_shader_vs.empty()) {
597 m_shadows_supported = false;
598 errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
602 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass2_fragment.glsl");
603 if (depth_shader_fs.empty()) {
604 m_shadows_supported = false;
605 errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
608 m_shadow_mix_cb = new shadowScreenQuadCB();
609 m_screen_quad = new shadowScreenQuad();
610 mixcsm_shader = gpu->addHighLevelShaderMaterial(
611 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
613 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
614 video::EPST_PS_1_2, m_shadow_mix_cb);
616 m_screen_quad->getMaterial().MaterialType =
617 (video::E_MATERIAL_TYPE)mixcsm_shader;
619 if (mixcsm_shader == -1) {
620 // upsi, something went wrong loading shader.
621 delete m_shadow_mix_cb;
622 delete m_screen_quad;
623 m_shadows_supported = false;
624 errorstream << "Error compiling cascade shadow mapping shader." << std::endl;
628 // HACK, TODO: investigate this better
629 // Grab the material renderer once more so minetest doesn't crash
631 m_driver->getMaterialRenderer(mixcsm_shader)->grab();
634 if (m_shadow_map_colored && depth_shader_trans == -1) {
635 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_trans_vertex.glsl");
636 if (depth_shader_vs.empty()) {
637 m_shadows_supported = false;
638 errorstream << "Error shadow mapping vs shader not found." << std::endl;
641 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_trans_fragment.glsl");
642 if (depth_shader_fs.empty()) {
643 m_shadows_supported = false;
644 errorstream << "Error shadow mapping fs shader not found." << std::endl;
647 m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
649 depth_shader_trans = gpu->addHighLevelShaderMaterial(
650 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
652 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
653 video::EPST_PS_1_2, m_shadow_depth_trans_cb);
655 if (depth_shader_trans == -1) {
656 // upsi, something went wrong loading shader.
657 delete m_shadow_depth_trans_cb;
658 m_shadow_depth_trans_cb = nullptr;
659 m_shadow_map_colored = false;
660 m_shadows_supported = false;
661 errorstream << "Error compiling colored shadow mapping shader." << std::endl;
665 // HACK, TODO: investigate this better
666 // Grab the material renderer once more so minetest doesn't crash
668 m_driver->getMaterialRenderer(depth_shader_trans)->grab();
672 std::string ShadowRenderer::readShaderFile(const std::string &path)
675 if (m_shadow_map_colored)
676 prefix.append("#define COLORED_SHADOWS 1\n");
677 prefix.append("#line 0\n");
680 fs::ReadFile(path, content);
682 return prefix + content;