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.
21 #include "client/shadows/dynamicshadowsrender.h"
22 #include "client/shadows/shadowsScreenQuad.h"
23 #include "client/shadows/shadowsshadercallbacks.h"
26 #include "util/string.h"
27 #include "client/shader.h"
28 #include "client/client.h"
29 #include "client/clientmap.h"
31 ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
32 m_device(device), m_smgr(device->getSceneManager()),
33 m_driver(device->getVideoDriver()), m_client(client)
35 m_shadows_enabled = true;
37 m_shadow_strength = g_settings->getFloat("shadow_strength");
39 m_shadow_map_max_distance = g_settings->getFloat("shadow_map_max_distance");
41 m_shadow_map_texture_size = g_settings->getFloat("shadow_map_texture_size");
43 m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit");
44 m_shadow_map_colored = g_settings->getBool("shadow_map_color");
45 m_shadow_samples = g_settings->getS32("shadow_filters");
46 m_update_delta = g_settings->getFloat("shadow_update_time");
49 ShadowRenderer::~ShadowRenderer()
51 if (m_shadow_depth_cb)
52 delete m_shadow_depth_cb;
54 delete m_shadow_mix_cb;
55 m_shadow_node_array.clear();
58 if (shadowMapTextureDynamicObjects)
59 m_driver->removeTexture(shadowMapTextureDynamicObjects);
61 if (shadowMapTextureFinal)
62 m_driver->removeTexture(shadowMapTextureFinal);
64 if (shadowMapTextureColors)
65 m_driver->removeTexture(shadowMapTextureColors);
67 if (shadowMapClientMap)
68 m_driver->removeTexture(shadowMapClientMap);
71 void ShadowRenderer::initialize()
73 auto *gpu = m_driver->getGPUProgrammingServices();
76 if (m_shadows_enabled && gpu && m_driver->queryFeature(video::EVDF_ARB_GLSL)) {
79 m_shadows_enabled = false;
81 warningstream << "Shadows: GLSL Shader not supported on this system."
86 m_texture_format = m_shadow_map_texture_32bit
87 ? video::ECOLOR_FORMAT::ECF_R32F
88 : video::ECOLOR_FORMAT::ECF_R16F;
90 m_texture_format_color = m_shadow_map_texture_32bit
91 ? video::ECOLOR_FORMAT::ECF_G32R32F
92 : video::ECOLOR_FORMAT::ECF_G16R16F;
96 float ShadowRenderer::getUpdateDelta() const
98 return m_update_delta;
101 size_t ShadowRenderer::addDirectionalLight()
103 m_light_list.emplace_back(m_shadow_map_texture_size,
105 video::SColor(255, 255, 255, 255), m_shadow_map_max_distance);
106 return m_light_list.size() - 1;
109 DirectionalLight &ShadowRenderer::getDirectionalLight(u32 index)
111 return m_light_list[index];
114 size_t ShadowRenderer::getDirectionalLightCount() const
116 return m_light_list.size();
119 f32 ShadowRenderer::getMaxShadowFar() const
121 if (!m_light_list.empty()) {
122 float wanted_range = m_client->getEnv().getClientMap().getWantedRange();
124 float zMax = m_light_list[0].getMaxFarValue() > wanted_range
126 : m_light_list[0].getMaxFarValue();
127 return zMax * MAP_BLOCKSIZE;
132 void ShadowRenderer::addNodeToShadowList(
133 scene::ISceneNode *node, E_SHADOW_MODE shadowMode)
135 m_shadow_node_array.emplace_back(NodeToApply(node, shadowMode));
138 void ShadowRenderer::removeNodeFromShadowList(scene::ISceneNode *node)
140 for (auto it = m_shadow_node_array.begin(); it != m_shadow_node_array.end();) {
141 if (it->node == node) {
142 it = m_shadow_node_array.erase(it);
150 void ShadowRenderer::setClearColor(video::SColor ClearColor)
152 m_clear_color = ClearColor;
155 void ShadowRenderer::update(video::ITexture *outputTarget)
157 if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
162 if (!shadowMapTextureDynamicObjects) {
164 shadowMapTextureDynamicObjects = getSMTexture(
165 std::string("shadow_dynamic_") + itos(m_shadow_map_texture_size),
166 m_texture_format, true);
169 if (!shadowMapClientMap) {
171 shadowMapClientMap = getSMTexture(
172 std::string("shadow_clientmap_") + itos(m_shadow_map_texture_size),
173 m_shadow_map_colored ? m_texture_format_color : m_texture_format,
177 if (m_shadow_map_colored && !shadowMapTextureColors) {
178 shadowMapTextureColors = getSMTexture(
179 std::string("shadow_colored_") + itos(m_shadow_map_texture_size),
180 m_shadow_map_colored ? m_texture_format_color : m_texture_format,
184 // The merge all shadowmaps texture
185 if (!shadowMapTextureFinal) {
186 video::ECOLOR_FORMAT frt;
187 if (m_shadow_map_texture_32bit) {
188 if (m_shadow_map_colored)
189 frt = video::ECOLOR_FORMAT::ECF_A32B32G32R32F;
191 frt = video::ECOLOR_FORMAT::ECF_R32F;
193 if (m_shadow_map_colored)
194 frt = video::ECOLOR_FORMAT::ECF_A16B16G16R16F;
196 frt = video::ECOLOR_FORMAT::ECF_R16F;
198 shadowMapTextureFinal = getSMTexture(
199 std::string("shadowmap_final_") + itos(m_shadow_map_texture_size),
203 if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
204 // for every directional light:
205 for (DirectionalLight &light : m_light_list) {
206 // Static shader values.
207 m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size;
208 m_shadow_depth_cb->MaxFar = (f32)m_shadow_map_max_distance * BS;
210 // set the Render Target
211 // right now we can only render in usual RTT, not
212 // Depth texture is available in irrlicth maybe we
213 // should put some gl* fn here
215 if (light.should_update_map_shadow) {
216 light.should_update_map_shadow = false;
218 m_driver->setRenderTarget(shadowMapClientMap, true, true,
219 video::SColor(255, 255, 255, 255));
220 renderShadowMap(shadowMapClientMap, light);
222 if (m_shadow_map_colored) {
223 m_driver->setRenderTarget(shadowMapTextureColors,
224 true, false, video::SColor(255, 255, 255, 255));
226 renderShadowMap(shadowMapTextureColors, light,
227 scene::ESNRP_TRANSPARENT);
228 m_driver->setRenderTarget(0, false, false);
231 // render shadows for the n0n-map objects.
232 m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
233 true, video::SColor(255, 255, 255, 255));
234 renderShadowObjects(shadowMapTextureDynamicObjects, light);
235 // clear the Render Target
236 m_driver->setRenderTarget(0, false, false);
238 // in order to avoid too many map shadow renders,
239 // we should make a second pass to mix clientmap shadows and
240 // entities shadows :(
241 m_screen_quad->getMaterial().setTexture(0, shadowMapClientMap);
242 // dynamic objs shadow texture.
243 if (m_shadow_map_colored)
244 m_screen_quad->getMaterial().setTexture(1, shadowMapTextureColors);
245 m_screen_quad->getMaterial().setTexture(2, shadowMapTextureDynamicObjects);
247 m_driver->setRenderTarget(shadowMapTextureFinal, false, false,
248 video::SColor(255, 255, 255, 255));
249 m_screen_quad->render(m_driver);
250 m_driver->setRenderTarget(0, false, false);
254 // now render the actual MT render pass
255 m_driver->setRenderTarget(outputTarget, true, true, m_clear_color);
258 /* this code just shows shadows textures in screen and in ONLY for debugging*/
260 // this is debug, ignore for now.
261 m_driver->draw2DImage(shadowMapTextureFinal,
262 core::rect<s32>(0, 50, 128, 128 + 50),
263 core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
265 m_driver->draw2DImage(shadowMapClientMap,
266 core::rect<s32>(0, 50 + 128, 128, 128 + 50 + 128),
267 core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
268 m_driver->draw2DImage(shadowMapTextureDynamicObjects,
269 core::rect<s32>(0, 128 + 50 + 128, 128,
270 128 + 50 + 128 + 128),
271 core::rect<s32>({0, 0}, shadowMapTextureDynamicObjects->getSize()));
273 if (m_shadow_map_colored) {
275 m_driver->draw2DImage(shadowMapTextureColors,
276 core::rect<s32>(128,128 + 50 + 128 + 128,
277 128 + 128, 128 + 50 + 128 + 128 + 128),
278 core::rect<s32>({0, 0}, shadowMapTextureColors->getSize()));
281 m_driver->setRenderTarget(0, false, false);
286 video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name,
287 video::ECOLOR_FORMAT texture_format, bool force_creation)
289 if (force_creation) {
290 return m_driver->addRenderTargetTexture(
291 core::dimension2du(m_shadow_map_texture_size,
292 m_shadow_map_texture_size),
293 shadow_map_name.c_str(), texture_format);
296 return m_driver->getTexture(shadow_map_name.c_str());
299 void ShadowRenderer::renderShadowMap(video::ITexture *target,
300 DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass)
302 m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
303 m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
305 // Operate on the client map
306 for (const auto &shadow_node : m_shadow_node_array) {
307 if (strcmp(shadow_node.node->getName(), "ClientMap") != 0)
310 ClientMap *map_node = static_cast<ClientMap *>(shadow_node.node);
312 video::SMaterial material;
313 if (map_node->getMaterialCount() > 0) {
314 // we only want the first material, which is the one with the albedo info
315 material = map_node->getMaterial(0);
318 material.BackfaceCulling = false;
319 material.FrontfaceCulling = true;
320 material.PolygonOffsetFactor = 4.0f;
321 material.PolygonOffsetDirection = video::EPO_BACK;
322 //material.PolygonOffsetDepthBias = 1.0f/4.0f;
323 //material.PolygonOffsetSlopeScale = -1.f;
325 if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
326 material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
328 material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
330 // FIXME: I don't think this is needed here
331 map_node->OnAnimate(m_device->getTimer()->getTime());
333 m_driver->setTransform(video::ETS_WORLD,
334 map_node->getAbsoluteTransformation());
336 map_node->renderMapShadows(m_driver, material, pass);
341 void ShadowRenderer::renderShadowObjects(
342 video::ITexture *target, DirectionalLight &light)
344 m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
345 m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
347 for (const auto &shadow_node : m_shadow_node_array) {
348 // we only take care of the shadow casters
349 if (shadow_node.shadowMode == ESM_RECEIVE ||
350 strcmp(shadow_node.node->getName(), "ClientMap") == 0)
353 // render other objects
354 u32 n_node_materials = shadow_node.node->getMaterialCount();
355 std::vector<s32> BufferMaterialList;
356 std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
357 BufferMaterialList.reserve(n_node_materials);
358 BufferMaterialCullingList.reserve(n_node_materials);
360 // backup materialtype for each material
362 // and replace it by our "depth" shader
363 for (u32 m = 0; m < n_node_materials; m++) {
364 auto ¤t_mat = shadow_node.node->getMaterial(m);
366 BufferMaterialList.push_back(current_mat.MaterialType);
367 current_mat.MaterialType =
368 (video::E_MATERIAL_TYPE)depth_shader;
370 current_mat.setTexture(3, shadowMapTextureFinal);
372 BufferMaterialCullingList.emplace_back(
373 (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
375 current_mat.BackfaceCulling = true;
376 current_mat.FrontfaceCulling = false;
377 current_mat.PolygonOffsetFactor = 1.0f/2048.0f;
378 current_mat.PolygonOffsetDirection = video::EPO_BACK;
379 //current_mat.PolygonOffsetDepthBias = 1.0 * 2.8e-6;
380 //current_mat.PolygonOffsetSlopeScale = -1.f;
383 m_driver->setTransform(video::ETS_WORLD,
384 shadow_node.node->getAbsoluteTransformation());
385 shadow_node.node->render();
387 // restore the material.
389 for (u32 m = 0; m < n_node_materials; m++) {
390 auto ¤t_mat = shadow_node.node->getMaterial(m);
392 current_mat.MaterialType = (video::E_MATERIAL_TYPE) BufferMaterialList[m];
394 current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
395 current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
398 } // end for caster shadow nodes
401 void ShadowRenderer::mixShadowsQuad()
406 * @Liso's disclaimer ;) This function loads the Shadow Mapping Shaders.
407 * I used a custom loader because I couldn't figure out how to use the base
408 * Shaders system with custom IShaderConstantSetCallBack without messing up the
409 * code too much. If anyone knows how to integrate this with the standard MT
410 * shaders, please feel free to change it.
413 void ShadowRenderer::createShaders()
415 video::IGPUProgrammingServices *gpu = m_driver->getGPUProgrammingServices();
417 if (depth_shader == -1) {
418 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl");
419 if (depth_shader_vs.empty()) {
420 m_shadows_enabled = false;
421 errorstream << "Error shadow mapping vs shader not found." << std::endl;
424 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl");
425 if (depth_shader_fs.empty()) {
426 m_shadows_enabled = false;
427 errorstream << "Error shadow mapping fs shader not found." << std::endl;
430 m_shadow_depth_cb = new ShadowDepthShaderCB();
432 depth_shader = gpu->addHighLevelShaderMaterial(
433 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
435 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
436 video::EPST_PS_1_2, m_shadow_depth_cb);
438 if (depth_shader == -1) {
439 // upsi, something went wrong loading shader.
440 delete m_shadow_depth_cb;
441 m_shadows_enabled = false;
442 errorstream << "Error compiling shadow mapping shader." << std::endl;
446 // HACK, TODO: investigate this better
447 // Grab the material renderer once more so minetest doesn't crash
449 m_driver->getMaterialRenderer(depth_shader)->grab();
452 if (mixcsm_shader == -1) {
453 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
454 if (depth_shader_vs.empty()) {
455 m_shadows_enabled = false;
456 errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
460 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass2_fragment.glsl");
461 if (depth_shader_fs.empty()) {
462 m_shadows_enabled = false;
463 errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
466 m_shadow_mix_cb = new shadowScreenQuadCB();
467 m_screen_quad = new shadowScreenQuad();
468 mixcsm_shader = gpu->addHighLevelShaderMaterial(
469 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
471 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
472 video::EPST_PS_1_2, m_shadow_mix_cb);
474 m_screen_quad->getMaterial().MaterialType =
475 (video::E_MATERIAL_TYPE)mixcsm_shader;
477 if (mixcsm_shader == -1) {
478 // upsi, something went wrong loading shader.
479 delete m_shadow_mix_cb;
480 delete m_screen_quad;
481 m_shadows_enabled = false;
482 errorstream << "Error compiling cascade shadow mapping shader." << std::endl;
486 // HACK, TODO: investigate this better
487 // Grab the material renderer once more so minetest doesn't crash
489 m_driver->getMaterialRenderer(mixcsm_shader)->grab();
492 if (m_shadow_map_colored && depth_shader_trans == -1) {
493 std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_trans_vertex.glsl");
494 if (depth_shader_vs.empty()) {
495 m_shadows_enabled = false;
496 errorstream << "Error shadow mapping vs shader not found." << std::endl;
499 std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_trans_fragment.glsl");
500 if (depth_shader_fs.empty()) {
501 m_shadows_enabled = false;
502 errorstream << "Error shadow mapping fs shader not found." << std::endl;
505 m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
507 depth_shader_trans = gpu->addHighLevelShaderMaterial(
508 readShaderFile(depth_shader_vs).c_str(), "vertexMain",
510 readShaderFile(depth_shader_fs).c_str(), "pixelMain",
511 video::EPST_PS_1_2, m_shadow_depth_trans_cb);
513 if (depth_shader_trans == -1) {
514 // upsi, something went wrong loading shader.
515 delete m_shadow_depth_trans_cb;
516 m_shadow_map_colored = false;
517 m_shadows_enabled = false;
518 errorstream << "Error compiling colored shadow mapping shader." << std::endl;
522 // HACK, TODO: investigate this better
523 // Grab the material renderer once more so minetest doesn't crash
525 m_driver->getMaterialRenderer(depth_shader_trans)->grab();
529 std::string ShadowRenderer::readShaderFile(const std::string &path)
532 if (m_shadow_map_colored)
533 prefix.append("#define COLORED_SHADOWS 1\n");
536 fs::ReadFile(path, content);
538 return prefix + content;