Shadow mapping render pass (#11244)

Co-authored-by: x2048 <codeforsmile@gmail.com>
This commit is contained in:
Liso 2021-06-06 18:51:21 +02:00 committed by GitHub
parent 46f42e15c4
commit c47313db65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2624 additions and 38 deletions

@ -43,6 +43,14 @@ local labels = {
fgettext("2x"), fgettext("2x"),
fgettext("4x"), fgettext("4x"),
fgettext("8x") fgettext("8x")
},
shadow_levels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Ultra High")
} }
} }
@ -66,6 +74,10 @@ local dd_options = {
antialiasing = { antialiasing = {
table.concat(labels.antialiasing, ","), table.concat(labels.antialiasing, ","),
{"0", "2", "4", "8"} {"0", "2", "4", "8"}
},
shadow_levels = {
table.concat(labels.shadow_levels, ","),
{ "0", "1", "2", "3", "4", "5" }
} }
} }
@ -110,6 +122,15 @@ local getSettingIndex = {
end end
end end
return 1 return 1
end,
ShadowMapping = function()
local shadow_setting = core.settings:get("shadow_levels")
for i = 1, #dd_options.shadow_levels[2] do
if shadow_setting == dd_options.shadow_levels[2][i] then
return i
end
end
return 1
end end
} }
@ -197,7 +218,10 @@ local function formspec(tabview, name, tabdata)
"checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" "checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]"..
"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" ..
"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
.. getSettingIndex.ShadowMapping() .. "]"
else else
tab_string = tab_string .. tab_string = tab_string ..
"label[8.38,0.7;" .. core.colorize("#888888", "label[8.38,0.7;" .. core.colorize("#888888",
@ -207,7 +231,9 @@ local function formspec(tabview, name, tabdata)
"label[8.38,1.7;" .. core.colorize("#888888", "label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Waving Leaves")) .. "]" .. fgettext("Waving Leaves")) .. "]" ..
"label[8.38,2.2;" .. core.colorize("#888888", "label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Waving Plants")) .. "]" fgettext("Waving Plants")) .. "]"..
"label[8.38,2.7;" .. core.colorize("#888888",
fgettext("Dynamic shadows")) .. "]"
end end
return tab_string return tab_string
@ -333,6 +359,34 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
ddhandled = true ddhandled = true
end end
for i = 1, #labels.shadow_levels do
if fields["dd_shadows"] == labels.shadow_levels[i] then
core.settings:set("shadow_levels", dd_options.shadow_levels[2][i])
ddhandled = true
end
end
if fields["dd_shadows"] == labels.shadow_levels[1] then
core.settings:set("enable_dynamic_shadows", "false")
else
core.settings:set("enable_dynamic_shadows", "true")
local shadow_presets = {
[2] = { 80, 512, "true", 0, "false" },
[3] = { 120, 1024, "true", 1, "false" },
[4] = { 350, 2048, "true", 1, "false" },
[5] = { 350, 2048, "true", 2, "true" },
[6] = { 450, 4096, "true", 2, "true" },
}
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
if s then
core.settings:set("shadow_map_max_distance", s[1])
core.settings:set("shadow_map_texture_size", s[2])
core.settings:set("shadow_map_texture_32bit", s[3])
core.settings:set("shadow_filters", s[4])
core.settings:set("shadow_map_color", s[5])
end
end
return ddhandled return ddhandled
end end

@ -582,6 +582,58 @@ enable_waving_leaves (Waving leaves) bool false
# Requires shaders to be enabled. # Requires shaders to be enabled.
enable_waving_plants (Waving plants) bool false enable_waving_plants (Waving plants) bool false
[***Dynamic shadows]
# Set to true to enable Shadow Mapping.
# Requires shaders to be enabled.
enable_dynamic_shadows (Dynamic shadows) bool false
# Set the shadow strength.
# Lower value means lighter shadows, higher value means darker shadows.
shadow_strength (Shadow strength) float 0.2 0.05 1.0
# Maximum distance to render shadows.
shadow_map_max_distance (Shadow map max distance in nodes to render shadows) float 120.0 10.0 1000.0
# Texture size to render the shadow map on.
# This must be a power of two.
# Bigger numbers create better shadowsbut it is also more expensive.
shadow_map_texture_size (Shadow map texture size) int 1024 128 8192
# Sets shadow texture quality to 32 bits.
# On false, 16 bits texture will be used.
# This can cause much more artifacts in the shadow.
shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true
# Enable poisson disk filtering.
# On true uses poisson disk to make "soft shadows". Otherwise uses PCF filtering.
shadow_poisson_filter (Poisson filtering) bool true
# Define shadow filtering quality
# This simulates the soft shadows effect by applying a PCF or poisson disk
# but also uses more resources.
shadow_filters (Shadow filter quality) enum 1 0,1,2
# Enable colored shadows.
# On true translucent nodes cast colored shadows. This is expensive.
shadow_map_color (Colored shadows) bool false
# Set the shadow update time.
# Lower value means shadows and map updates faster, but it consume more resources.
# Minimun value 0.001 seconds max value 0.2 seconds
shadow_update_time (Map update time) float 0.2 0.001 0.2
# Set the soft shadow radius size.
# Lower values mean sharper shadows bigger values softer.
# Minimun value 1.0 and max value 10.0
shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0
# Set the tilt of Sun/Moon orbit in degrees
# Value of 0 means no tilt / vertical orbit.
# Minimun value 0.0 and max value 60.0
shadow_sky_body_orbit_tilt (Sky Body Orbit Tilt) float 0.0 0.0 60.0
[**Advanced] [**Advanced]
# Arm inertia, gives a more realistic movement of # Arm inertia, gives a more realistic movement of

@ -7,7 +7,22 @@ uniform vec3 eyePosition;
// The cameraOffset is the current center of the visible world. // The cameraOffset is the current center of the visible world.
uniform vec3 cameraOffset; uniform vec3 cameraOffset;
uniform float animationTimer; uniform float animationTimer;
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
uniform sampler2D ShadowMapSampler;
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float cosLight;
varying float f_normal_length;
#endif
varying vec3 vNormal;
varying vec3 vPosition; varying vec3 vPosition;
// World position in the visible world (i.e. relative to the cameraOffset.) // World position in the visible world (i.e. relative to the cameraOffset.)
// This can be used for many shader effects without loss of precision. // This can be used for many shader effects without loss of precision.
@ -22,10 +37,388 @@ varying mediump vec2 varTexCoord;
centroid varying vec2 varTexCoord; centroid varying vec2 varTexCoord;
#endif #endif
varying vec3 eyeVec; varying vec3 eyeVec;
varying float nightRatio;
const float fogStart = FOG_START; const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / ( 1.0 - fogStart); const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
#ifdef ENABLE_DYNAMIC_SHADOWS
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
// assuming near is always 1.0
float getLinearDepth()
{
return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
}
vec3 getLightSpacePosition()
{
vec4 pLightSpace;
// some drawtypes have zero normals, so we need to handle it :(
#if DRAW_TYPE == NDT_PLANTLIKE
pLightSpace = m_ShadowViewProj * vec4(worldPosition, 1.0);
#else
float offsetScale = (0.0057 * getLinearDepth() + normalOffsetScale);
pLightSpace = m_ShadowViewProj * vec4(worldPosition + offsetScale * normalize(vNormal), 1.0);
#endif
pLightSpace = getPerspectiveFactor(pLightSpace);
return pLightSpace.xyz * 0.5 + 0.5;
}
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
vec3 unpackColor(float value)
{
vec3 color;
color.b = mod(value, c_precisionp1) / c_precision;
color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
return color;
}
vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
float visibility = step(0.0, realDistance - texDepth.r);
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
if (visibility < 0.1) {
visibility = step(0.0, realDistance - texDepth.b);
result = vec4(visibility, unpackColor(texDepth.a));
}
return result;
}
#else
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float visibility = step(0.0, realDistance - texDepth);
return visibility;
}
#endif
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif
#ifdef COLORED_SHADOWS
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
return depth;
}
#else
float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float depth = realDistance - texDepth;
return depth;
}
#endif
float getBaseLength(vec2 smTexCoord)
{
float l = length(2.0 * smTexCoord.xy - 1.0); // length in texture coords
return bias1 / (1.0 / l - bias0); // return to undistorted coords
}
float getDeltaPerspectiveFactor(float l)
{
return 0.1 / (bias0 * l + bias1); // original distortion factor, divided by 10
}
float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
{
// Return fast if sharp shadows are requested
if (SOFTSHADOWRADIUS <= 1.0)
return SOFTSHADOWRADIUS;
vec2 clampedpos;
float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
float y, x;
float depth = 0.0;
float pointDepth;
float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
int n = 0;
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y);
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
if (pointDepth > -0.01) {
depth += pointDepth;
n += 1;
}
}
depth = depth / n;
depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
return max(0.5, depth * maxRadius);
}
#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
vec2(0.170019, -0.040254),
vec2(-0.299417, 0.791925),
vec2(0.645680, 0.493210),
vec2(-0.651784, 0.717887),
vec2(0.421003, 0.027070),
vec2(-0.817194, -0.271096),
vec2(-0.705374, -0.668203),
vec2(0.977050, -0.108615),
vec2(0.063326, 0.142369),
vec2(0.203528, 0.214331),
vec2(-0.667531, 0.326090),
vec2(-0.098422, -0.295755),
vec2(-0.885922, 0.215369),
vec2(0.566637, 0.605213),
vec2(0.039766, -0.396100),
vec2(0.751946, 0.453352),
vec2(0.078707, -0.715323),
vec2(-0.075838, -0.529344),
vec2(0.724479, -0.580798),
vec2(0.222999, -0.215125),
vec2(-0.467574, -0.405438),
vec2(-0.248268, -0.814753),
vec2(0.354411, -0.887570),
vec2(0.175817, 0.382366),
vec2(0.487472, -0.063082),
vec2(0.355476, 0.025357),
vec2(-0.084078, 0.898312),
vec2(0.488876, -0.783441),
vec2(0.470016, 0.217933),
vec2(-0.696890, -0.549791),
vec2(-0.149693, 0.605762),
vec2(0.034211, 0.979980),
vec2(0.503098, -0.308878),
vec2(-0.016205, -0.872921),
vec2(0.385784, -0.393902),
vec2(-0.146886, -0.859249),
vec2(0.643361, 0.164098),
vec2(0.634388, -0.049471),
vec2(-0.688894, 0.007843),
vec2(0.464034, -0.188818),
vec2(-0.440840, 0.137486),
vec2(0.364483, 0.511704),
vec2(0.034028, 0.325968),
vec2(0.099094, -0.308023),
vec2(0.693960, -0.366253),
vec2(0.678884, -0.204688),
vec2(0.001801, 0.780328),
vec2(0.145177, -0.898984),
vec2(0.062655, -0.611866),
vec2(0.315226, -0.604297),
vec2(-0.780145, 0.486251),
vec2(-0.371868, 0.882138),
vec2(0.200476, 0.494430),
vec2(-0.494552, -0.711051),
vec2(0.612476, 0.705252),
vec2(-0.578845, -0.768792),
vec2(-0.772454, -0.090976),
vec2(0.504440, 0.372295),
vec2(0.155736, 0.065157),
vec2(0.391522, 0.849605),
vec2(-0.620106, -0.328104),
vec2(0.789239, -0.419965),
vec2(-0.545396, 0.538133),
vec2(-0.178564, -0.596057)
);
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES));
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x];
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / samples;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES));
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
int end_offset = int(samples) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x];
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / samples;
}
#endif
#else
/* poisson filter disabled */
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
int n = 0;
// basic PCF filter
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
n += 1;
}
return visibility / n;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
if (radius < 0.1) {
// we are in the middle of even brightness, no need for filtering
return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
}
float baseLength = getBaseLength(smTexCoord);
float perspectiveFactor;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND);
int n = 0;
// basic PCF filter
for (y = -bound; y <= bound; y += 1.0)
for (x = -bound; x <= bound; x += 1.0) {
clampedpos = vec2(x,y); // screen offset
perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
n += 1;
}
return visibility / n;
}
#endif
#endif
#endif
#if ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters /* Hable's UC2 Tone mapping parameters
@ -58,6 +451,8 @@ vec4 applyToneMapping(vec4 color)
} }
#endif #endif
void main(void) void main(void)
{ {
vec3 color; vec3 color;
@ -74,9 +469,41 @@ void main(void)
#endif #endif
color = base.rgb; color = base.rgb;
vec4 col = vec4(color.rgb * varColor.rgb, 1.0); vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
float shadow_int = 0.0;
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
vec3 posLightSpace = getLightSpacePosition();
float distance_rate = (1 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 20.0));
float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z ),0.0);
if (distance_rate > 1e-7) {
#ifdef COLORED_SHADOWS
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
shadow_int = visibility.r;
shadow_color = visibility.gba;
#else
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
#endif
shadow_int *= distance_rate;
shadow_int *= 1.0 - nightRatio;
}
if (f_normal_length != 0 && cosLight < 0.0) {
shadow_int = clamp(1.0-nightRatio, 0.0, 1.0);
}
shadow_int = 1.0 - (shadow_int * f_adj_shadow_strength);
col.rgb = mix(shadow_color,col.rgb,shadow_int)*shadow_int;
// col.r = 0.5 * clamp(getPenumbraRadius(ShadowMapSampler, posLightSpace.xy, posLightSpace.z, 1.0) / SOFTSHADOWRADIUS, 0.0, 1.0) + 0.5 * col.r;
#endif
#if ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
col = applyToneMapping(col); col = applyToneMapping(col);
#endif #endif

@ -1,5 +1,4 @@
uniform mat4 mWorld; uniform mat4 mWorld;
// Color of the light emitted by the sun. // Color of the light emitted by the sun.
uniform vec3 dayLight; uniform vec3 dayLight;
uniform vec3 eyePosition; uniform vec3 eyePosition;
@ -8,6 +7,7 @@ uniform vec3 eyePosition;
uniform vec3 cameraOffset; uniform vec3 cameraOffset;
uniform float animationTimer; uniform float animationTimer;
varying vec3 vNormal;
varying vec3 vPosition; varying vec3 vPosition;
// World position in the visible world (i.e. relative to the cameraOffset.) // World position in the visible world (i.e. relative to the cameraOffset.)
// This can be used for many shader effects without loss of precision. // This can be used for many shader effects without loss of precision.
@ -24,13 +24,38 @@ varying mediump vec2 varTexCoord;
#else #else
centroid varying vec2 varTexCoord; centroid varying vec2 varTexCoord;
#endif #endif
varying vec3 eyeVec; #ifdef ENABLE_DYNAMIC_SHADOWS
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_shadow_strength;
uniform float f_timeofday;
varying float cosLight;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float f_normal_length;
#endif
varying vec3 eyeVec;
varying float nightRatio;
// Color of the light emitted by the light sources. // Color of the light emitted by the light sources.
const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
const float e = 2.718281828459; const float e = 2.718281828459;
const float BS = 10.0; const float BS = 10.0;
#ifdef ENABLE_DYNAMIC_SHADOWS
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#endif
float smoothCurve(float x) float smoothCurve(float x)
{ {
@ -86,6 +111,9 @@ float snoise(vec3 p)
#endif #endif
void main(void) void main(void)
{ {
varTexCoord = inTexCoord0.st; varTexCoord = inTexCoord0.st;
@ -136,10 +164,9 @@ void main(void)
gl_Position = mWorldViewProj * inVertexPosition; gl_Position = mWorldViewProj * inVertexPosition;
#endif #endif
vPosition = gl_Position.xyz; vPosition = gl_Position.xyz;
eyeVec = -(mWorldView * inVertexPosition).xyz; eyeVec = -(mWorldView * inVertexPosition).xyz;
vNormal = inVertexNormal;
// Calculate color. // Calculate color.
// Red, green and blue components are pre-multiplied with // Red, green and blue components are pre-multiplied with
@ -152,7 +179,7 @@ void main(void)
vec4 color = inVertexColor; vec4 color = inVertexColor;
#endif #endif
// The alpha gives the ratio of sunlight in the incoming light. // The alpha gives the ratio of sunlight in the incoming light.
float nightRatio = 1.0 - color.a; nightRatio = 1.0 - color.a;
color.rgb = color.rgb * (color.a * dayLight.rgb + color.rgb = color.rgb * (color.a * dayLight.rgb +
nightRatio * artificialLight.rgb) * 2.0; nightRatio * artificialLight.rgb) * 2.0;
color.a = 1.0; color.a = 1.0;
@ -164,4 +191,26 @@ void main(void)
0.07 * brightness); 0.07 * brightness);
varColor = clamp(color, 0.0, 1.0); varColor = clamp(color, 0.0, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
vec3 nNormal = normalize(vNormal);
cosLight = dot(nNormal, -v_LightDirection);
float texelSize = 767.0 / f_textureresolution;
float slopeScale = clamp(1.0 - abs(cosLight), 0.0, 1.0);
normalOffsetScale = texelSize * slopeScale;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
} else if (f_timeofday >= 0.8) {
adj_shadow_strength = f_shadow_strength * 0.5 *
mtsmoothstep(0.8, 0.83, f_timeofday);
} else {
adj_shadow_strength = f_shadow_strength *
mtsmoothstep(0.20, 0.25, f_timeofday) *
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
}
f_normal_length = length(vNormal);
#endif
} }

@ -23,8 +23,22 @@ const float BS = 10.0;
const float fogStart = FOG_START; const float fogStart = FOG_START;
const float fogShadingParameter = 1.0 / (1.0 - fogStart); const float fogShadingParameter = 1.0 / (1.0 - fogStart);
#if ENABLE_TONE_MAPPING #ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
uniform sampler2D ShadowMapSampler;
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_timeofday;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float cosLight;
varying float f_normal_length;
#endif
#if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters /* Hable's UC2 Tone mapping parameters
A = 0.22; A = 0.22;
B = 0.30; B = 0.30;
@ -55,11 +69,263 @@ vec4 applyToneMapping(vec4 color)
} }
#endif #endif
#ifdef ENABLE_DYNAMIC_SHADOWS
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
// assuming near is always 1.0
float getLinearDepth()
{
return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
}
vec3 getLightSpacePosition()
{
vec4 pLightSpace;
float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale;
pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0);
pLightSpace = getPerspectiveFactor(pLightSpace);
return pLightSpace.xyz * 0.5 + 0.5;
}
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
vec3 unpackColor(float value)
{
vec3 color;
color.b = mod(value, c_precisionp1) / c_precision;
color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
return color;
}
vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
float visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
if (visibility < 0.1) {
visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
result = vec4(visibility, unpackColor(texDepth.a));
}
return result;
}
#else
float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
float visibility = step(0.0, (realDistance-2e-5) - texDepth);
return visibility;
}
#endif
#if SHADOW_FILTER == 2
#define PCFBOUND 3.5
#define PCFSAMPLES 64.0
#elif SHADOW_FILTER == 1
#define PCFBOUND 1.5
#if defined(POISSON_FILTER)
#define PCFSAMPLES 32.0
#else
#define PCFSAMPLES 16.0
#endif
#else
#define PCFBOUND 0.0
#if defined(POISSON_FILTER)
#define PCFSAMPLES 4.0
#else
#define PCFSAMPLES 1.0
#endif
#endif
#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
vec2(0.170019, -0.040254),
vec2(-0.299417, 0.791925),
vec2(0.645680, 0.493210),
vec2(-0.651784, 0.717887),
vec2(0.421003, 0.027070),
vec2(-0.817194, -0.271096),
vec2(-0.705374, -0.668203),
vec2(0.977050, -0.108615),
vec2(0.063326, 0.142369),
vec2(0.203528, 0.214331),
vec2(-0.667531, 0.326090),
vec2(-0.098422, -0.295755),
vec2(-0.885922, 0.215369),
vec2(0.566637, 0.605213),
vec2(0.039766, -0.396100),
vec2(0.751946, 0.453352),
vec2(0.078707, -0.715323),
vec2(-0.075838, -0.529344),
vec2(0.724479, -0.580798),
vec2(0.222999, -0.215125),
vec2(-0.467574, -0.405438),
vec2(-0.248268, -0.814753),
vec2(0.354411, -0.887570),
vec2(0.175817, 0.382366),
vec2(0.487472, -0.063082),
vec2(0.355476, 0.025357),
vec2(-0.084078, 0.898312),
vec2(0.488876, -0.783441),
vec2(0.470016, 0.217933),
vec2(-0.696890, -0.549791),
vec2(-0.149693, 0.605762),
vec2(0.034211, 0.979980),
vec2(0.503098, -0.308878),
vec2(-0.016205, -0.872921),
vec2(0.385784, -0.393902),
vec2(-0.146886, -0.859249),
vec2(0.643361, 0.164098),
vec2(0.634388, -0.049471),
vec2(-0.688894, 0.007843),
vec2(0.464034, -0.188818),
vec2(-0.440840, 0.137486),
vec2(0.364483, 0.511704),
vec2(0.034028, 0.325968),
vec2(0.099094, -0.308023),
vec2(0.693960, -0.366253),
vec2(0.678884, -0.204688),
vec2(0.001801, 0.780328),
vec2(0.145177, -0.898984),
vec2(0.062655, -0.611866),
vec2(0.315226, -0.604297),
vec2(-0.780145, 0.486251),
vec2(-0.371868, 0.882138),
vec2(0.200476, 0.494430),
vec2(-0.494552, -0.711051),
vec2(0.612476, 0.705252),
vec2(-0.578845, -0.768792),
vec2(-0.772454, -0.090976),
vec2(0.504440, 0.372295),
vec2(0.155736, 0.065157),
vec2(0.391522, 0.849605),
vec2(-0.620106, -0.328104),
vec2(0.789239, -0.419965),
vec2(-0.545396, 0.538133),
vec2(-0.178564, -0.596057)
);
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float texture_size = 1.0 / (f_textureresolution * 0.5);
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
int end_offset = int(PCFSAMPLES) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float texture_size = 1.0 / (f_textureresolution * 0.5);
int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
int end_offset = int(PCFSAMPLES) + init_offset;
for (int x = init_offset; x < end_offset; x++) {
clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#endif
#else
/* poisson filter disabled */
#ifdef COLORED_SHADOWS
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
vec4 visibility = vec4(0.0);
float sradius=0.0;
if( PCFBOUND>0)
sradius = SOFTSHADOWRADIUS / PCFBOUND;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
// basic PCF filter
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
clampedpos = vec2(x,y) * texture_size* sradius + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
vec2 clampedpos;
float visibility = 0.0;
float sradius=0.0;
if( PCFBOUND>0)
sradius = SOFTSHADOWRADIUS / PCFBOUND;
float texture_size = 1.0 / (f_textureresolution * 0.5);
float y, x;
// basic PCF filter
for (y = -PCFBOUND; y <= PCFBOUND; y += 1.0)
for (x = -PCFBOUND; x <= PCFBOUND; x += 1.0) {
clampedpos = vec2(x,y) * texture_size * sradius + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
return visibility / PCFSAMPLES;
}
#endif
#endif
#endif
void main(void) void main(void)
{ {
vec3 color; vec3 color;
vec2 uv = varTexCoord.st; vec2 uv = varTexCoord.st;
vec4 base = texture2D(baseTexture, uv).rgba; vec4 base = texture2D(baseTexture, uv).rgba;
#ifdef USE_DISCARD #ifdef USE_DISCARD
@ -72,13 +338,34 @@ void main(void)
#endif #endif
color = base.rgb; color = base.rgb;
vec4 col = vec4(color.rgb, base.a); vec4 col = vec4(color.rgb, base.a);
col.rgb *= varColor.rgb; col.rgb *= varColor.rgb;
col.rgb *= emissiveColor.rgb * vIDiff; col.rgb *= emissiveColor.rgb * vIDiff;
#ifdef ENABLE_DYNAMIC_SHADOWS
float shadow_int = 0.0;
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
vec3 posLightSpace = getLightSpacePosition();
#ifdef COLORED_SHADOWS
vec4 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
shadow_int = visibility.r;
shadow_color = visibility.gba;
#else
shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
#endif
if (f_normal_length != 0 && cosLight <= 0.001) {
shadow_int = clamp(shadow_int + 0.5 * abs(cosLight), 0.0, 1.0);
}
shadow_int = 1.0 - (shadow_int * adj_shadow_strength);
col.rgb = mix(shadow_color, col.rgb, shadow_int) * shadow_int;
#endif
#if ENABLE_TONE_MAPPING #if ENABLE_TONE_MAPPING
col = applyToneMapping(col); col = applyToneMapping(col);
#endif #endif

@ -13,12 +13,37 @@ varying mediump vec2 varTexCoord;
centroid varying vec2 varTexCoord; centroid varying vec2 varTexCoord;
#endif #endif
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow uniforms
uniform vec3 v_LightDirection;
uniform float f_textureresolution;
uniform mat4 m_ShadowViewProj;
uniform float f_shadowfar;
uniform float f_shadow_strength;
uniform float f_timeofday;
varying float cosLight;
varying float normalOffsetScale;
varying float adj_shadow_strength;
varying float f_normal_length;
#endif
varying vec3 eyeVec; varying vec3 eyeVec;
varying float vIDiff; varying float vIDiff;
const float e = 2.718281828459; const float e = 2.718281828459;
const float BS = 10.0; const float BS = 10.0;
#ifdef ENABLE_DYNAMIC_SHADOWS
// custom smoothstep implementation because it's not defined in glsl1.2
// https://docs.gl/sl4/smoothstep
float mtsmoothstep(in float edge0, in float edge1, in float x)
{
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
#endif
float directional_ambient(vec3 normal) float directional_ambient(vec3 normal)
{ {
vec3 v = normal * normal; vec3 v = normal * normal;
@ -54,4 +79,25 @@ void main(void)
#else #else
varColor = inVertexColor; varColor = inVertexColor;
#endif #endif
#ifdef ENABLE_DYNAMIC_SHADOWS
cosLight = max(0.0, dot(vNormal, -v_LightDirection));
float texelSize = 0.51;
float slopeScale = clamp(1.0 - cosLight, 0.0, 1.0);
normalOffsetScale = texelSize * slopeScale;
if (f_timeofday < 0.2) {
adj_shadow_strength = f_shadow_strength * 0.5 *
(1.0 - mtsmoothstep(0.18, 0.2, f_timeofday));
} else if (f_timeofday >= 0.8) {
adj_shadow_strength = f_shadow_strength * 0.5 *
mtsmoothstep(0.8, 0.83, f_timeofday);
} else {
adj_shadow_strength = f_shadow_strength *
mtsmoothstep(0.20, 0.25, f_timeofday) *
(1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
}
f_normal_length = length(vNormal);
#endif
} }

@ -0,0 +1,13 @@
uniform sampler2D ColorMapSampler;
varying vec4 tPos;
void main()
{
vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st);
if (col.a < 0.70)
discard;
float depth = 0.5 + tPos.z * 0.5;
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
}

@ -0,0 +1,38 @@
uniform sampler2D ColorMapSampler;
varying vec4 tPos;
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
const float c_precision = 128.0;
const float c_precisionp1 = c_precision + 1.0;
float packColor(vec3 color)
{
return floor(color.b * c_precision + 0.5)
+ floor(color.g * c_precision + 0.5) * c_precisionp1
+ floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
}
const vec3 black = vec3(0.0);
#endif
void main()
{
vec4 col = texture2D(ColorMapSampler, gl_TexCoord[0].st);
#ifndef COLORED_SHADOWS
if (col.a < 0.5)
discard;
#endif
float depth = 0.5 + tPos.z * 0.5;
// ToDo: Liso: Apply movement on waving plants
// depth in [0, 1] for texture
//col.rgb = col.a == 1.0 ? vec3(1.0) : col.rgb;
#ifdef COLORED_SHADOWS
float packedColor = packColor(mix(col.rgb, black, col.a));
gl_FragColor = vec4(depth, packedColor, 0.0,1.0);
#else
gl_FragColor = vec4(depth, 0.0, 0.0, 1.0);
#endif
}

@ -0,0 +1,26 @@
uniform mat4 LightMVP; // world matrix
varying vec4 tPos;
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
void main()
{
vec4 pos = LightMVP * gl_Vertex;
tPos = getPerspectiveFactor(LightMVP * gl_Vertex);
gl_Position = vec4(tPos.xyz, 1.0);
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
}

@ -0,0 +1,26 @@
uniform mat4 LightMVP; // world matrix
varying vec4 tPos;
const float bias0 = 0.9;
const float zPersFactor = 0.5;
const float bias1 = 1.0 - bias0 + 1e-6;
vec4 getPerspectiveFactor(in vec4 shadowPosition)
{
float pDistance = length(shadowPosition.xy);
float pFactor = pDistance * bias0 + bias1;
shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
return shadowPosition;
}
void main()
{
vec4 pos = LightMVP * gl_Vertex;
tPos = getPerspectiveFactor(pos);
gl_Position = vec4(tPos.xyz, 1.0);
gl_TexCoord[0].st = gl_MultiTexCoord0.st;
}

@ -0,0 +1,23 @@
uniform sampler2D ShadowMapClientMap;
#ifdef COLORED_SHADOWS
uniform sampler2D ShadowMapClientMapTraslucent;
#endif
uniform sampler2D ShadowMapSamplerdynamic;
void main() {
#ifdef COLORED_SHADOWS
vec2 first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).rg;
vec2 depth_splitdynamics = vec2(texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r, 0.0);
if (first_depth.r > depth_splitdynamics.r)
first_depth = depth_splitdynamics;
vec2 depth_color = texture2D(ShadowMapClientMapTraslucent, gl_TexCoord[1].st).rg;
gl_FragColor = vec4(first_depth.r, first_depth.g, depth_color.r, depth_color.g);
#else
float first_depth = texture2D(ShadowMapClientMap, gl_TexCoord[0].st).r;
float depth_splitdynamics = texture2D(ShadowMapSamplerdynamic, gl_TexCoord[2].st).r;
first_depth = min(first_depth, depth_splitdynamics);
gl_FragColor = vec4(first_depth, 0.0, 0.0, 1.0);
#endif
}

@ -0,0 +1,9 @@
void main()
{
vec4 uv = vec4(gl_Vertex.xyz, 1.0) * 0.5 + 0.5;
gl_TexCoord[0] = uv;
gl_TexCoord[1] = uv;
gl_TexCoord[2] = uv;
gl_Position = vec4(gl_Vertex.xyz, 1.0);
}

@ -58,5 +58,9 @@ set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/sky.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sky.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/wieldmesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wieldmesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/shadows/dynamicshadows.cpp
${CMAKE_CURRENT_SOURCE_DIR}/shadows/dynamicshadowsrender.cpp
${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsshadercallbacks.cpp
${CMAKE_CURRENT_SOURCE_DIR}/shadows/shadowsScreenQuad.cpp
PARENT_SCOPE PARENT_SCOPE
) )

@ -72,8 +72,15 @@ ClientMap::ClientMap(
scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(), scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
rendering_engine->get_scene_manager(), id), rendering_engine->get_scene_manager(), id),
m_client(client), m_client(client),
m_rendering_engine(rendering_engine),
m_control(control) m_control(control)
{ {
/*
* @Liso: Sadly C++ doesn't have introspection, so the only way we have to know
* the class is whith a name ;) Name property cames from ISceneNode base class.
*/
Name = "ClientMap";
m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000, m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000); BS*1000000,BS*1000000,BS*1000000);
@ -115,12 +122,21 @@ void ClientMap::OnRegisterSceneNode()
} }
ISceneNode::OnRegisterSceneNode(); ISceneNode::OnRegisterSceneNode();
if (!m_added_to_shadow_renderer) {
m_added_to_shadow_renderer = true;
if (auto shadows = m_rendering_engine->get_shadow_renderer())
shadows->addNodeToShadowList(this);
}
} }
void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
v3s16 *p_blocks_min, v3s16 *p_blocks_max) v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range)
{ {
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1); if (range <= 0.0f)
range = m_control.wanted_range;
v3s16 box_nodes_d = range * v3s16(1, 1, 1);
// Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d' // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
// can exceed the range of v3s16 when a large view range is used near the // can exceed the range of v3s16 when a large view range is used near the
// world edges. // world edges.
@ -321,7 +337,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Mesh animation // Mesh animation
if (pass == scene::ESNRP_SOLID) { if (pass == scene::ESNRP_SOLID) {
//MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh; MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh); assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely // Pretty random but this should work somewhat nicely
@ -342,8 +357,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Get the meshbuffers of the block Get the meshbuffers of the block
*/ */
{ {
//MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh; MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh); assert(mapBlockMesh);
@ -394,6 +407,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
"returning." << std::endl; "returning." << std::endl;
return; return;
} }
// pass the shadow map texture to the buffer texture
ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
if (shadow && shadow->is_active()) {
auto &layer = list.m.TextureLayer[3];
layer.Texture = shadow->get_texture();
layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
layer.TrilinearFilter = true;
}
driver->setMaterial(list.m); driver->setMaterial(list.m);
drawcall_count += list.bufs.size(); drawcall_count += list.bufs.size();
@ -610,3 +634,187 @@ void ClientMap::PrintInfo(std::ostream &out)
{ {
out<<"ClientMap: "; out<<"ClientMap: ";
} }
void ClientMap::renderMapShadows(video::IVideoDriver *driver,
const video::SMaterial &material, s32 pass)
{
bool is_transparent_pass = pass != scene::ESNRP_SOLID;
std::string prefix;
if (is_transparent_pass)
prefix = "renderMap(SHADOW TRANS): ";
else
prefix = "renderMap(SHADOW SOLID): ";
u32 drawcall_count = 0;
u32 vertex_count = 0;
MeshBufListList drawbufs;
for (auto &i : m_drawlist_shadow) {
v3s16 block_pos = i.first;
MapBlock *block = i.second;
// If the mesh of the block happened to get deleted, ignore it
if (!block->mesh)
continue;
/*
Get the meshbuffers of the block
*/
{
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial &mat = buf->getMaterial();
auto rnd = driver->getMaterialRenderer(mat.MaterialType);
bool transparent = rnd && rnd->isTransparent();
if (transparent == is_transparent_pass)
drawbufs.add(buf, block_pos, layer);
}
}
}
}
TimeTaker draw("Drawing shadow mesh buffers");
core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS);
// Render all layers in order
for (auto &lists : drawbufs.lists) {
for (MeshBufList &list : lists) {
// Check and abort if the machine is swapping a lot
if (draw.getTimerTime() > 1000) {
infostream << "ClientMap::renderMapShadows(): Rendering "
"took >1s, returning." << std::endl;
break;
}
for (auto &pair : list.bufs) {
scene::IMeshBuffer *buf = pair.second;
// override some material properties
video::SMaterial local_material = buf->getMaterial();
local_material.MaterialType = material.MaterialType;
local_material.BackfaceCulling = material.BackfaceCulling;
local_material.FrontfaceCulling = material.FrontfaceCulling;
local_material.Lighting = false;
driver->setMaterial(local_material);
v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m);
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
}
drawcall_count += list.bufs.size();
}
}
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
}
/*
Custom update draw list for the pov of shadow light.
*/
void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
{
ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
const v3f camera_position = shadow_light_pos;
const v3f camera_direction = shadow_light_dir;
// I "fake" fov just to avoid creating a new function to handle orthographic
// projection.
const f32 camera_fov = m_camera_fov * 1.9f;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 p_blocks_min;
v3s16 p_blocks_max;
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, shadow_range);
std::vector<v2s16> blocks_in_range;
for (auto &i : m_drawlist_shadow) {
MapBlock *block = i.second;
block->refDrop();
}
m_drawlist_shadow.clear();
// We need to append the blocks from the camera POV because sometimes
// they are not inside the light frustum and it creates glitches.
// FIXME: This could be removed if we figure out why they are missing
// from the light frustum.
for (auto &i : m_drawlist) {
i.second->refGrab();
m_drawlist_shadow[i.first] = i.second;
}
// Number of blocks currently loaded by the client
u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
u32 blocks_in_range_with_mesh = 0;
// Number of blocks occlusion culled
u32 blocks_occlusion_culled = 0;
for (auto &sector_it : m_sectors) {
MapSector *sector = sector_it.second;
if (!sector)
continue;
blocks_loaded += sector->size();
MapBlockVect sectorblocks;
sector->getBlocks(sectorblocks);
/*
Loop through blocks in sector
*/
for (MapBlock *block : sectorblocks) {
if (!block->mesh) {
// Ignore if mesh doesn't exist
continue;
}
float range = shadow_range;
float d = 0.0;
if (!isBlockInSight(block->getPos(), camera_position,
camera_direction, camera_fov, range, &d))
continue;
blocks_in_range_with_mesh++;
/*
Occlusion culling
*/
if (isBlockOccluded(block, cam_pos_nodes)) {
blocks_occlusion_culled++;
continue;
}
// This block is in range. Reset usage timer.
block->resetUsageTimer();
// Add to set
if (m_drawlist_shadow.find(block->getPos()) == m_drawlist_shadow.end()) {
block->refGrab();
m_drawlist_shadow[block->getPos()] = block;
}
}
}
g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
g_profiler->avg("SHADOW MapBlocks occlusion culled [#]", blocks_occlusion_culled);
g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
}

@ -119,10 +119,14 @@ public:
} }
void getBlocksInViewRange(v3s16 cam_pos_nodes, void getBlocksInViewRange(v3s16 cam_pos_nodes,
v3s16 *p_blocks_min, v3s16 *p_blocks_max); v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range=-1.0f);
void updateDrawList(); void updateDrawList();
void updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range);
void renderMap(video::IVideoDriver* driver, s32 pass); void renderMap(video::IVideoDriver* driver, s32 pass);
void renderMapShadows(video::IVideoDriver *driver,
const video::SMaterial &material, s32 pass);
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);
@ -132,9 +136,12 @@ public:
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
const MapDrawControl & getControl() const { return m_control; } const MapDrawControl & getControl() const { return m_control; }
f32 getWantedRange() const { return m_control.wanted_range; }
f32 getCameraFov() const { return m_camera_fov; } f32 getCameraFov() const { return m_camera_fov; }
private: private:
Client *m_client; Client *m_client;
RenderingEngine *m_rendering_engine;
aabb3f m_box = aabb3f(-BS * 1000000, -BS * 1000000, -BS * 1000000, aabb3f m_box = aabb3f(-BS * 1000000, -BS * 1000000, -BS * 1000000,
BS * 1000000, BS * 1000000, BS * 1000000); BS * 1000000, BS * 1000000, BS * 1000000);
@ -147,10 +154,12 @@ private:
v3s16 m_camera_offset; v3s16 m_camera_offset;
std::map<v3s16, MapBlock*> m_drawlist; std::map<v3s16, MapBlock*> m_drawlist;
std::map<v3s16, MapBlock*> m_drawlist_shadow;
std::set<v2s16> m_last_drawn_sectors; std::set<v2s16> m_last_drawn_sectors;
bool m_cache_trilinear_filter; bool m_cache_trilinear_filter;
bool m_cache_bilinear_filter; bool m_cache_bilinear_filter;
bool m_cache_anistropic_filter; bool m_cache_anistropic_filter;
bool m_added_to_shadow_renderer{false};
}; };

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IMeshManipulator.h> #include <IMeshManipulator.h>
#include <IAnimatedMeshSceneNode.h> #include <IAnimatedMeshSceneNode.h>
#include "client/client.h" #include "client/client.h"
#include "client/renderingengine.h"
#include "client/sound.h" #include "client/sound.h"
#include "client/tile.h" #include "client/tile.h"
#include "util/basic_macros.h" #include "util/basic_macros.h"
@ -555,6 +556,9 @@ void GenericCAO::removeFromScene(bool permanent)
clearParentAttachment(); clearParentAttachment();
} }
if (auto shadow = RenderingEngine::get_shadow_renderer())
shadow->removeNodeFromShadowList(getSceneNode());
if (m_meshnode) { if (m_meshnode) {
m_meshnode->remove(); m_meshnode->remove();
m_meshnode->drop(); m_meshnode->drop();
@ -803,11 +807,14 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
if (m_reset_textures_timer < 0) if (m_reset_textures_timer < 0)
updateTextures(m_current_texture_modifier); updateTextures(m_current_texture_modifier);
scene::ISceneNode *node = getSceneNode(); if (scene::ISceneNode *node = getSceneNode()) {
if (m_matrixnode)
if (node && m_matrixnode)
node->setParent(m_matrixnode); node->setParent(m_matrixnode);
if (auto shadow = RenderingEngine::get_shadow_renderer())
shadow->addNodeToShadowList(node);
}
updateNametag(); updateNametag();
updateMarker(); updateMarker();
updateNodePos(); updateNodePos();

@ -738,6 +738,7 @@ protected:
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
const CameraOrientation &cam); const CameraOrientation &cam);
void updateShadows();
// Misc // Misc
void limitFps(FpsControl *fps_timings, f32 *dtime); void limitFps(FpsControl *fps_timings, f32 *dtime);
@ -3831,13 +3832,20 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
*/ */
runData.update_draw_list_timer += dtime; runData.update_draw_list_timer += dtime;
float update_draw_list_delta = 0.2f;
if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer())
update_draw_list_delta = shadow->getUpdateDelta();
v3f camera_direction = camera->getDirection(); v3f camera_direction = camera->getDirection();
if (runData.update_draw_list_timer >= 0.2 if (runData.update_draw_list_timer >= update_draw_list_delta
|| runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
|| m_camera_offset_changed) { || m_camera_offset_changed) {
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;
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);
@ -3968,7 +3976,34 @@ inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
graph->put(values); graph->put(values);
} }
/****************************************************************************
* Shadows
*****************************************************************************/
void Game::updateShadows()
{
ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer();
if (!shadow)
return;
float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
float timeoftheday = fmod(getWickedTimeOfDay(in_timeofday) + 0.75f, 0.5f) + 0.25f;
const float offset_constant = 10000.0f;
v3f light(0.0f, 0.0f, -1.0f);
light.rotateXZBy(90);
light.rotateXYBy(timeoftheday * 360 - 90);
light.rotateYZBy(sky->getSkyBodyOrbitTilt());
v3f sun_pos = light * offset_constant;
if (shadow->getDirectionalLightCount() == 0)
shadow->addDirectionalLight();
shadow->getDirectionalLight().setDirection(sun_pos);
shadow->setTimeOfDay(in_timeofday);
shadow->getDirectionalLight().update_frustum(camera, client);
}
/**************************************************************************** /****************************************************************************
Misc Misc

@ -860,6 +860,9 @@ static void updateFastFaceRow(
g_settings->getBool("enable_shaders") && g_settings->getBool("enable_shaders") &&
g_settings->getBool("enable_waving_water"); g_settings->getBool("enable_waving_water");
static thread_local const bool force_not_tiling =
g_settings->getBool("enable_dynamic_shadows");
v3s16 p = startpos; v3s16 p = startpos;
u16 continuous_tiles_count = 1; u16 continuous_tiles_count = 1;
@ -898,7 +901,8 @@ static void updateFastFaceRow(
waving, waving,
next_tile); next_tile);
if (next_makes_face == makes_face if (!force_not_tiling
&& next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir && next_p_corrected == p_corrected + translate_dir
&& next_face_dir_corrected == face_dir_corrected && next_face_dir_corrected == face_dir_corrected
&& memcmp(next_lights, lights, sizeof(lights)) == 0 && memcmp(next_lights, lights, sizeof(lights)) == 0

@ -24,25 +24,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/clientmap.h" #include "client/clientmap.h"
#include "client/hud.h" #include "client/hud.h"
#include "client/minimap.h" #include "client/minimap.h"
#include "client/shadows/dynamicshadowsrender.h"
RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud) RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud)
: device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()), : device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()),
guienv(device->getGUIEnvironment()), client(_client), camera(client->getCamera()), guienv(device->getGUIEnvironment()), client(_client), camera(client->getCamera()),
mapper(client->getMinimap()), hud(_hud) mapper(client->getMinimap()), hud(_hud),
shadow_renderer(nullptr)
{ {
screensize = driver->getScreenSize(); screensize = driver->getScreenSize();
virtual_size = screensize; virtual_size = screensize;
if (g_settings->getBool("enable_shaders") &&
g_settings->getBool("enable_dynamic_shadows")) {
shadow_renderer = new ShadowRenderer(device, client);
}
} }
RenderingCore::~RenderingCore() RenderingCore::~RenderingCore()
{ {
clearTextures(); clearTextures();
delete shadow_renderer;
} }
void RenderingCore::initialize() void RenderingCore::initialize()
{ {
// have to be called late as the VMT is not ready in the constructor: // have to be called late as the VMT is not ready in the constructor:
initTextures(); initTextures();
if (shadow_renderer)
shadow_renderer->initialize();
} }
void RenderingCore::updateScreenSize() void RenderingCore::updateScreenSize()
@ -72,7 +82,14 @@ void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_min
void RenderingCore::draw3D() void RenderingCore::draw3D()
{ {
if (shadow_renderer) {
// Shadow renderer will handle the draw stage
shadow_renderer->setClearColor(skycolor);
shadow_renderer->update();
} else {
smgr->drawAll(); smgr->drawAll();
}
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
if (!show_hud) if (!show_hud)
return; return;

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once #pragma once
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
class ShadowRenderer;
class Camera; class Camera;
class Client; class Client;
class Hud; class Hud;
@ -47,6 +48,8 @@ protected:
Minimap *mapper; Minimap *mapper;
Hud *hud; Hud *hud;
ShadowRenderer *shadow_renderer;
void updateScreenSize(); void updateScreenSize();
virtual void initTextures() {} virtual void initTextures() {}
virtual void clearTextures() {} virtual void clearTextures() {}
@ -72,4 +75,6 @@ public:
bool _draw_wield_tool, bool _draw_crosshair); bool _draw_wield_tool, bool _draw_crosshair);
inline v2u32 getVirtualSize() const { return virtual_size; } inline v2u32 getVirtualSize() const { return virtual_size; }
ShadowRenderer *get_shadow_renderer() { return shadow_renderer; };
}; };

@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string> #include <string>
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "debug.h" #include "debug.h"
#include "client/render/core.h"
// include the shadow mapper classes too
#include "client/shadows/dynamicshadowsrender.h"
class ITextureSource; class ITextureSource;
class Camera; class Camera;
@ -113,6 +117,13 @@ public:
return m_device->run(); return m_device->run();
} }
// FIXME: this is still global when it shouldn't be
static ShadowRenderer *get_shadow_renderer()
{
if (s_singleton && s_singleton->core)
return s_singleton->core->get_shadow_renderer();
return nullptr;
}
static std::vector<core::vector3d<u32>> getSupportedVideoModes(); static std::vector<core::vector3d<u32>> getSupportedVideoModes();
static std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers(); static std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers();

@ -225,6 +225,16 @@ class MainShaderConstantSetter : public IShaderConstantSetter
{ {
CachedVertexShaderSetting<float, 16> m_world_view_proj; CachedVertexShaderSetting<float, 16> m_world_view_proj;
CachedVertexShaderSetting<float, 16> m_world; CachedVertexShaderSetting<float, 16> m_world;
// Shadow-related
CachedPixelShaderSetting<float, 16> m_shadow_view_proj;
CachedPixelShaderSetting<float, 3> m_light_direction;
CachedPixelShaderSetting<float> m_texture_res;
CachedPixelShaderSetting<float> m_shadow_strength;
CachedPixelShaderSetting<float> m_time_of_day;
CachedPixelShaderSetting<float> m_shadowfar;
CachedPixelShaderSetting<s32> m_shadow_texture;
#if ENABLE_GLES #if ENABLE_GLES
// Modelview matrix // Modelview matrix
CachedVertexShaderSetting<float, 16> m_world_view; CachedVertexShaderSetting<float, 16> m_world_view;
@ -243,6 +253,13 @@ public:
, m_texture("mTexture") , m_texture("mTexture")
, m_normal("mNormal") , m_normal("mNormal")
#endif #endif
, m_shadow_view_proj("m_ShadowViewProj")
, m_light_direction("v_LightDirection")
, m_texture_res("f_textureresolution")
, m_shadow_strength("f_shadow_strength")
, m_time_of_day("f_timeofday")
, m_shadowfar("f_shadowfar")
, m_shadow_texture("ShadowMapSampler")
{} {}
~MainShaderConstantSetter() = default; ~MainShaderConstantSetter() = default;
@ -280,6 +297,36 @@ public:
}; };
m_normal.set(m, services); m_normal.set(m, services);
#endif #endif
// Set uniforms for Shadow shader
if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
const auto &light = shadow->getDirectionalLight();
core::matrix4 shadowViewProj = light.getProjectionMatrix();
shadowViewProj *= light.getViewMatrix();
m_shadow_view_proj.set(shadowViewProj.pointer(), services);
float v_LightDirection[3];
light.getDirection().getAs3Values(v_LightDirection);
m_light_direction.set(v_LightDirection, services);
float TextureResolution = light.getMapResolution();
m_texture_res.set(&TextureResolution, services);
float ShadowStrength = shadow->getShadowStrength();
m_shadow_strength.set(&ShadowStrength, services);
float timeOfDay = shadow->getTimeOfDay();
m_time_of_day.set(&timeOfDay, services);
float shadowFar = shadow->getMaxShadowFar();
m_shadowfar.set(&shadowFar, services);
// I dont like using this hardcoded value. maybe something like
// MAX_TEXTURE - 1 or somthing like that??
s32 TextureLayerID = 3;
m_shadow_texture.set(&TextureLayerID, services);
}
} }
}; };
@ -682,6 +729,23 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n"; shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n";
if (g_settings->getBool("enable_dynamic_shadows")) {
shaders_header << "#define ENABLE_DYNAMIC_SHADOWS 1\n";
if (g_settings->getBool("shadow_map_color"))
shaders_header << "#define COLORED_SHADOWS 1\n";
if (g_settings->getBool("shadow_poisson_filter"))
shaders_header << "#define POISSON_FILTER 1\n";
s32 shadow_filter = g_settings->getS32("shadow_filters");
shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n";
float shadow_soft_radius = g_settings->getS32("shadow_soft_radius");
if (shadow_soft_radius < 1.0f)
shadow_soft_radius = 1.0f;
shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
}
std::string common_header = shaders_header.str(); std::string common_header = shaders_header.str();
std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl"); std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");

@ -0,0 +1,145 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cmath>
#include "client/shadows/dynamicshadows.h"
#include "client/client.h"
#include "client/clientenvironment.h"
#include "client/clientmap.h"
#include "client/camera.h"
using m4f = core::matrix4;
void DirectionalLight::createSplitMatrices(const Camera *cam)
{
float radius;
v3f newCenter;
v3f look = cam->getDirection();
v3f camPos2 = cam->getPosition();
v3f camPos = v3f(camPos2.X - cam->getOffset().X * BS,
camPos2.Y - cam->getOffset().Y * BS,
camPos2.Z - cam->getOffset().Z * BS);
camPos += look * shadow_frustum.zNear;
camPos2 += look * shadow_frustum.zNear;
float end = shadow_frustum.zNear + shadow_frustum.zFar;
newCenter = camPos + look * (shadow_frustum.zNear + 0.05f * end);
v3f world_center = camPos2 + look * (shadow_frustum.zNear + 0.05f * end);
// Create a vector to the frustum far corner
// @Liso: move all vars we can outside the loop.
float tanFovY = tanf(cam->getFovY() * 0.5f);
float tanFovX = tanf(cam->getFovX() * 0.5f);
const v3f &viewUp = cam->getCameraNode()->getUpVector();
// viewUp.normalize();
v3f viewRight = look.crossProduct(viewUp);
// viewRight.normalize();
v3f farCorner = look + viewRight * tanFovX + viewUp * tanFovY;
// Compute the frustumBoundingSphere radius
v3f boundVec = (camPos + farCorner * shadow_frustum.zFar) - newCenter;
radius = boundVec.getLength() * 2.0f;
// boundVec.getLength();
float vvolume = radius * 2.0f;
float texelsPerUnit = getMapResolution() / vvolume;
m4f mTexelScaling;
mTexelScaling.setScale(texelsPerUnit);
m4f mLookAt, mLookAtInv;
mLookAt.buildCameraLookAtMatrixLH(v3f(0.0f, 0.0f, 0.0f), -direction, v3f(0.0f, 1.0f, 0.0f));
mLookAt *= mTexelScaling;
mLookAtInv = mLookAt;
mLookAtInv.makeInverse();
v3f frustumCenter = newCenter;
mLookAt.transformVect(frustumCenter);
frustumCenter.X = floorf(frustumCenter.X); // clamp to texel increment
frustumCenter.Y = floorf(frustumCenter.Y); // clamp to texel increment
frustumCenter.Z = floorf(frustumCenter.Z);
mLookAtInv.transformVect(frustumCenter);
// probar radius multipliacdor en funcion del I, a menor I mas multiplicador
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
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);
}
DirectionalLight::DirectionalLight(const u32 shadowMapResolution,
const v3f &position, video::SColorf lightColor,
f32 farValue) :
diffuseColor(lightColor),
farPlane(farValue), mapRes(shadowMapResolution), pos(position)
{}
void DirectionalLight::update_frustum(const Camera *cam, Client *client)
{
should_update_map_shadow = true;
float zNear = cam->getCameraNode()->getNearValue();
float zFar = getMaxFarValue();
///////////////////////////////////
// update splits near and fars
shadow_frustum.zNear = zNear;
shadow_frustum.zFar = zFar;
// update shadow frustum
createSplitMatrices(cam);
// get the draw list for shadows
client->getEnv().getClientMap().updateDrawListShadow(
getPosition(), getDirection(), shadow_frustum.length);
should_update_map_shadow = true;
}
void DirectionalLight::setDirection(v3f dir)
{
direction = -dir;
direction.normalize();
}
v3f DirectionalLight::getPosition() const
{
return shadow_frustum.position;
}
const m4f &DirectionalLight::getViewMatrix() const
{
return shadow_frustum.ViewMat;
}
const m4f &DirectionalLight::getProjectionMatrix() const
{
return shadow_frustum.ProjOrthMat;
}
m4f DirectionalLight::getViewProjMatrix()
{
return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat;
}

@ -0,0 +1,102 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_bloated.h"
#include <matrix4.h>
#include "util/basic_macros.h"
class Camera;
class Client;
struct shadowFrustum
{
float zNear{0.0f};
float zFar{0.0f};
float length{0.0f};
core::matrix4 ProjOrthMat;
core::matrix4 ViewMat;
v3f position;
};
class DirectionalLight
{
public:
DirectionalLight(const u32 shadowMapResolution,
const v3f &position,
video::SColorf lightColor = video::SColor(0xffffffff),
f32 farValue = 100.0f);
~DirectionalLight() = default;
//DISABLE_CLASS_COPY(DirectionalLight)
void update_frustum(const Camera *cam, Client *client);
// when set direction is updated to negative normalized(direction)
void setDirection(v3f dir);
v3f getDirection() const{
return direction;
};
v3f getPosition() const;
/// Gets the light's matrices.
const core::matrix4 &getViewMatrix() const;
const core::matrix4 &getProjectionMatrix() const;
core::matrix4 getViewProjMatrix();
/// Gets the light's far value.
f32 getMaxFarValue() const
{
return farPlane;
}
/// Gets the light's color.
const video::SColorf &getLightColor() const
{
return diffuseColor;
}
/// Sets the light's color.
void setLightColor(const video::SColorf &lightColor)
{
diffuseColor = lightColor;
}
/// Gets the shadow map resolution for this light.
u32 getMapResolution() const
{
return mapRes;
}
bool should_update_map_shadow{true};
private:
void createSplitMatrices(const Camera *cam);
video::SColorf diffuseColor;
f32 farPlane;
u32 mapRes;
v3f pos;
v3f direction{0};
shadowFrustum shadow_frustum;
};

@ -0,0 +1,539 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cstring>
#include "client/shadows/dynamicshadowsrender.h"
#include "client/shadows/shadowsScreenQuad.h"
#include "client/shadows/shadowsshadercallbacks.h"
#include "settings.h"
#include "filesys.h"
#include "util/string.h"
#include "client/shader.h"
#include "client/client.h"
#include "client/clientmap.h"
ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
m_device(device), m_smgr(device->getSceneManager()),
m_driver(device->getVideoDriver()), m_client(client)
{
m_shadows_enabled = true;
m_shadow_strength = g_settings->getFloat("shadow_strength");
m_shadow_map_max_distance = g_settings->getFloat("shadow_map_max_distance");
m_shadow_map_texture_size = g_settings->getFloat("shadow_map_texture_size");
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");
}
ShadowRenderer::~ShadowRenderer()
{
if (m_shadow_depth_cb)
delete m_shadow_depth_cb;
if (m_shadow_mix_cb)
delete m_shadow_mix_cb;
m_shadow_node_array.clear();
m_light_list.clear();
if (shadowMapTextureDynamicObjects)
m_driver->removeTexture(shadowMapTextureDynamicObjects);
if (shadowMapTextureFinal)
m_driver->removeTexture(shadowMapTextureFinal);
if (shadowMapTextureColors)
m_driver->removeTexture(shadowMapTextureColors);
if (shadowMapClientMap)
m_driver->removeTexture(shadowMapClientMap);
}
void ShadowRenderer::initialize()
{
auto *gpu = m_driver->getGPUProgrammingServices();
// we need glsl
if (m_shadows_enabled && gpu && m_driver->queryFeature(video::EVDF_ARB_GLSL)) {
createShaders();
} else {
m_shadows_enabled = false;
warningstream << "Shadows: GLSL Shader not supported on this system."
<< std::endl;
return;
}
m_texture_format = m_shadow_map_texture_32bit
? video::ECOLOR_FORMAT::ECF_R32F
: video::ECOLOR_FORMAT::ECF_R16F;
m_texture_format_color = m_shadow_map_texture_32bit
? video::ECOLOR_FORMAT::ECF_G32R32F
: video::ECOLOR_FORMAT::ECF_G16R16F;
}
float ShadowRenderer::getUpdateDelta() const
{
return m_update_delta;
}
size_t ShadowRenderer::addDirectionalLight()
{
m_light_list.emplace_back(m_shadow_map_texture_size,
v3f(0.f, 0.f, 0.f),
video::SColor(255, 255, 255, 255), m_shadow_map_max_distance);
return m_light_list.size() - 1;
}
DirectionalLight &ShadowRenderer::getDirectionalLight(u32 index)
{
return m_light_list[index];
}
size_t ShadowRenderer::getDirectionalLightCount() const
{
return m_light_list.size();
}
f32 ShadowRenderer::getMaxShadowFar() const
{
if (!m_light_list.empty()) {
float wanted_range = m_client->getEnv().getClientMap().getWantedRange();
float zMax = m_light_list[0].getMaxFarValue() > wanted_range
? wanted_range
: m_light_list[0].getMaxFarValue();
return zMax * MAP_BLOCKSIZE;
}
return 0.0f;
}
void ShadowRenderer::addNodeToShadowList(
scene::ISceneNode *node, E_SHADOW_MODE shadowMode)
{
m_shadow_node_array.emplace_back(NodeToApply(node, shadowMode));
}
void ShadowRenderer::removeNodeFromShadowList(scene::ISceneNode *node)
{
for (auto it = m_shadow_node_array.begin(); it != m_shadow_node_array.end();) {
if (it->node == node) {
it = m_shadow_node_array.erase(it);
break;
} else {
++it;
}
}
}
void ShadowRenderer::setClearColor(video::SColor ClearColor)
{
m_clear_color = ClearColor;
}
void ShadowRenderer::update(video::ITexture *outputTarget)
{
if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) {
m_smgr->drawAll();
return;
}
if (!shadowMapTextureDynamicObjects) {
shadowMapTextureDynamicObjects = getSMTexture(
std::string("shadow_dynamic_") + itos(m_shadow_map_texture_size),
m_texture_format, true);
}
if (!shadowMapClientMap) {
shadowMapClientMap = getSMTexture(
std::string("shadow_clientmap_") + 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),
m_shadow_map_colored ? m_texture_format_color : m_texture_format,
true);
}
// The merge all shadowmaps texture
if (!shadowMapTextureFinal) {
video::ECOLOR_FORMAT frt;
if (m_shadow_map_texture_32bit) {
if (m_shadow_map_colored)
frt = video::ECOLOR_FORMAT::ECF_A32B32G32R32F;
else
frt = video::ECOLOR_FORMAT::ECF_R32F;
} else {
if (m_shadow_map_colored)
frt = video::ECOLOR_FORMAT::ECF_A16B16G16R16F;
else
frt = video::ECOLOR_FORMAT::ECF_R16F;
}
shadowMapTextureFinal = getSMTexture(
std::string("shadowmap_final_") + itos(m_shadow_map_texture_size),
frt, true);
}
if (!m_shadow_node_array.empty() && !m_light_list.empty()) {
// for every directional light:
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;
// set the Render Target
// right now we can only render in usual RTT, not
// 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,
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(shadowMapTextureColors, light,
scene::ESNRP_TRANSPARENT);
m_driver->setRenderTarget(0, false, false);
}
// render shadows for the n0n-map objects.
m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true,
true, video::SColor(255, 255, 255, 255));
renderShadowObjects(shadowMapTextureDynamicObjects, light);
// clear the Render Target
m_driver->setRenderTarget(0, false, false);
// in order to avoid too many map shadow renders,
// we should make a second pass to mix clientmap shadows and
// entities shadows :(
m_screen_quad->getMaterial().setTexture(0, shadowMapClientMap);
// dynamic objs shadow texture.
if (m_shadow_map_colored)
m_screen_quad->getMaterial().setTexture(1, shadowMapTextureColors);
m_screen_quad->getMaterial().setTexture(2, shadowMapTextureDynamicObjects);
m_driver->setRenderTarget(shadowMapTextureFinal, false, false,
video::SColor(255, 255, 255, 255));
m_screen_quad->render(m_driver);
m_driver->setRenderTarget(0, false, false);
} // end for lights
// now render the actual MT render pass
m_driver->setRenderTarget(outputTarget, true, true, m_clear_color);
m_smgr->drawAll();
/* this code just shows shadows textures in screen and in ONLY for debugging*/
#if 0
// this is debug, ignore for now.
m_driver->draw2DImage(shadowMapTextureFinal,
core::rect<s32>(0, 50, 128, 128 + 50),
core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
m_driver->draw2DImage(shadowMapClientMap,
core::rect<s32>(0, 50 + 128, 128, 128 + 50 + 128),
core::rect<s32>({0, 0}, shadowMapTextureFinal->getSize()));
m_driver->draw2DImage(shadowMapTextureDynamicObjects,
core::rect<s32>(0, 128 + 50 + 128, 128,
128 + 50 + 128 + 128),
core::rect<s32>({0, 0}, shadowMapTextureDynamicObjects->getSize()));
if (m_shadow_map_colored) {
m_driver->draw2DImage(shadowMapTextureColors,
core::rect<s32>(128,128 + 50 + 128 + 128,
128 + 128, 128 + 50 + 128 + 128 + 128),
core::rect<s32>({0, 0}, shadowMapTextureColors->getSize()));
}
#endif
m_driver->setRenderTarget(0, false, false);
}
}
video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name,
video::ECOLOR_FORMAT texture_format, bool force_creation)
{
if (force_creation) {
return m_driver->addRenderTargetTexture(
core::dimension2du(m_shadow_map_texture_size,
m_shadow_map_texture_size),
shadow_map_name.c_str(), texture_format);
}
return m_driver->getTexture(shadow_map_name.c_str());
}
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());
// Operate on the client map
for (const auto &shadow_node : m_shadow_node_array) {
if (strcmp(shadow_node.node->getName(), "ClientMap") != 0)
continue;
ClientMap *map_node = static_cast<ClientMap *>(shadow_node.node);
video::SMaterial material;
if (map_node->getMaterialCount() > 0) {
// we only want the first material, which is the one with the albedo info
material = map_node->getMaterial(0);
}
material.BackfaceCulling = false;
material.FrontfaceCulling = true;
material.PolygonOffsetFactor = 4.0f;
material.PolygonOffsetDirection = video::EPO_BACK;
//material.PolygonOffsetDepthBias = 1.0f/4.0f;
//material.PolygonOffsetSlopeScale = -1.f;
if (m_shadow_map_colored && pass != scene::ESNRP_SOLID)
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans;
else
material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader;
// FIXME: I don't think this is needed here
map_node->OnAnimate(m_device->getTimer()->getTime());
m_driver->setTransform(video::ETS_WORLD,
map_node->getAbsoluteTransformation());
map_node->renderMapShadows(m_driver, material, pass);
break;
}
}
void ShadowRenderer::renderShadowObjects(
video::ITexture *target, DirectionalLight &light)
{
m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix());
m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix());
for (const auto &shadow_node : m_shadow_node_array) {
// we only take care of the shadow casters
if (shadow_node.shadowMode == ESM_RECEIVE ||
strcmp(shadow_node.node->getName(), "ClientMap") == 0)
continue;
// render other objects
u32 n_node_materials = shadow_node.node->getMaterialCount();
std::vector<s32> BufferMaterialList;
std::vector<std::pair<bool, bool>> BufferMaterialCullingList;
BufferMaterialList.reserve(n_node_materials);
BufferMaterialCullingList.reserve(n_node_materials);
// backup materialtype for each material
// (aka shader)
// and replace it by our "depth" shader
for (u32 m = 0; m < n_node_materials; m++) {
auto &current_mat = shadow_node.node->getMaterial(m);
BufferMaterialList.push_back(current_mat.MaterialType);
current_mat.MaterialType =
(video::E_MATERIAL_TYPE)depth_shader;
current_mat.setTexture(3, shadowMapTextureFinal);
BufferMaterialCullingList.emplace_back(
(bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling);
current_mat.BackfaceCulling = true;
current_mat.FrontfaceCulling = false;
current_mat.PolygonOffsetFactor = 1.0f/2048.0f;
current_mat.PolygonOffsetDirection = video::EPO_BACK;
//current_mat.PolygonOffsetDepthBias = 1.0 * 2.8e-6;
//current_mat.PolygonOffsetSlopeScale = -1.f;
}
m_driver->setTransform(video::ETS_WORLD,
shadow_node.node->getAbsoluteTransformation());
shadow_node.node->render();
// restore the material.
for (u32 m = 0; m < n_node_materials; m++) {
auto &current_mat = shadow_node.node->getMaterial(m);
current_mat.MaterialType = (video::E_MATERIAL_TYPE) BufferMaterialList[m];
current_mat.BackfaceCulling = BufferMaterialCullingList[m].first;
current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second;
}
} // end for caster shadow nodes
}
void ShadowRenderer::mixShadowsQuad()
{
}
/*
* @Liso's disclaimer ;) This function loads the Shadow Mapping Shaders.
* I used a custom loader because I couldn't figure out how to use the base
* Shaders system with custom IShaderConstantSetCallBack without messing up the
* code too much. If anyone knows how to integrate this with the standard MT
* shaders, please feel free to change it.
*/
void ShadowRenderer::createShaders()
{
video::IGPUProgrammingServices *gpu = m_driver->getGPUProgrammingServices();
if (depth_shader == -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;
}
m_shadow_depth_cb = new ShadowDepthShaderCB();
depth_shader = 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 == -1) {
// upsi, something went wrong loading shader.
delete m_shadow_depth_cb;
m_shadows_enabled = false;
errorstream << "Error compiling shadow mapping shader." << 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)->grab();
}
if (mixcsm_shader == -1) {
std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl");
if (depth_shader_vs.empty()) {
m_shadows_enabled = false;
errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
return;
}
std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass2_fragment.glsl");
if (depth_shader_fs.empty()) {
m_shadows_enabled = false;
errorstream << "Error cascade shadow mapping fs shader not found." << std::endl;
return;
}
m_shadow_mix_cb = new shadowScreenQuadCB();
m_screen_quad = new shadowScreenQuad();
mixcsm_shader = 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_mix_cb);
m_screen_quad->getMaterial().MaterialType =
(video::E_MATERIAL_TYPE)mixcsm_shader;
if (mixcsm_shader == -1) {
// upsi, something went wrong loading shader.
delete m_shadow_mix_cb;
delete m_screen_quad;
m_shadows_enabled = false;
errorstream << "Error compiling cascade shadow mapping shader." << std::endl;
return;
}
// HACK, TODO: investigate this better
// Grab the material renderer once more so minetest doesn't crash
// on exit
m_driver->getMaterialRenderer(mixcsm_shader)->grab();
}
if (m_shadow_map_colored && depth_shader_trans == -1) {
std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_trans_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_trans_fragment.glsl");
if (depth_shader_fs.empty()) {
m_shadows_enabled = false;
errorstream << "Error shadow mapping fs shader not found." << std::endl;
return;
}
m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
depth_shader_trans = 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_trans_cb);
if (depth_shader_trans == -1) {
// upsi, something went wrong loading shader.
delete m_shadow_depth_trans_cb;
m_shadow_map_colored = false;
m_shadows_enabled = false;
errorstream << "Error compiling colored shadow mapping shader." << 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_trans)->grab();
}
}
std::string ShadowRenderer::readShaderFile(const std::string &path)
{
std::string prefix;
if (m_shadow_map_colored)
prefix.append("#define COLORED_SHADOWS 1\n");
std::string content;
fs::ReadFile(path, content);
return prefix + content;
}

@ -0,0 +1,146 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
#include <vector>
#include "irrlichttypes_extrabloated.h"
#include "client/shadows/dynamicshadows.h"
class ShadowDepthShaderCB;
class shadowScreenQuad;
class shadowScreenQuadCB;
enum E_SHADOW_MODE : u8
{
ESM_RECEIVE = 0,
ESM_BOTH,
};
struct NodeToApply
{
NodeToApply(scene::ISceneNode *n,
E_SHADOW_MODE m = E_SHADOW_MODE::ESM_BOTH) :
node(n),
shadowMode(m){};
bool operator<(const NodeToApply &other) const { return node < other.node; };
scene::ISceneNode *node;
E_SHADOW_MODE shadowMode{E_SHADOW_MODE::ESM_BOTH};
bool dirty{false};
};
class ShadowRenderer
{
public:
ShadowRenderer(IrrlichtDevice *device, Client *client);
~ShadowRenderer();
void initialize();
/// Adds a directional light shadow map (Usually just one (the sun) except in
/// Tattoine ).
size_t addDirectionalLight();
DirectionalLight &getDirectionalLight(u32 index = 0);
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
/// ESM_RECEIVE only receives but does not cast shadows.
///
void addNodeToShadowList(scene::ISceneNode *node,
E_SHADOW_MODE shadowMode = ESM_BOTH);
void removeNodeFromShadowList(scene::ISceneNode *node);
void setClearColor(video::SColor ClearColor);
void update(video::ITexture *outputTarget = nullptr);
video::ITexture *get_texture()
{
return shadowMapTextureFinal;
}
bool is_active() const { return m_shadows_enabled; }
void setTimeOfDay(float isDay) { m_time_day = isDay; };
s32 getShadowSamples() const { return m_shadow_samples; }
float getShadowStrength() const { return m_shadow_strength; }
float getTimeOfDay() const { return m_time_day; }
private:
video::ITexture *getSMTexture(const std::string &shadow_map_name,
video::ECOLOR_FORMAT texture_format,
bool force_creation = false);
void renderShadowMap(video::ITexture *target, DirectionalLight &light,
scene::E_SCENE_NODE_RENDER_PASS pass =
scene::ESNRP_SOLID);
void renderShadowObjects(video::ITexture *target, DirectionalLight &light);
void mixShadowsQuad();
// a bunch of variables
IrrlichtDevice *m_device{nullptr};
scene::ISceneManager *m_smgr{nullptr};
video::IVideoDriver *m_driver{nullptr};
Client *m_client{nullptr};
video::ITexture *shadowMapClientMap{nullptr};
video::ITexture *shadowMapTextureFinal{nullptr};
video::ITexture *shadowMapTextureDynamicObjects{nullptr};
video::ITexture *shadowMapTextureColors{nullptr};
video::SColor m_clear_color{0x0};
std::vector<DirectionalLight> m_light_list;
std::vector<NodeToApply> m_shadow_node_array;
float m_shadow_strength;
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;
video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F};
video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16};
// Shadow Shader stuff
void createShaders();
std::string readShaderFile(const std::string &path);
s32 depth_shader{-1};
s32 depth_shader_trans{-1};
s32 mixcsm_shader{-1};
ShadowDepthShaderCB *m_shadow_depth_cb{nullptr};
ShadowDepthShaderCB *m_shadow_depth_trans_cb{nullptr};
shadowScreenQuad *m_screen_quad{nullptr};
shadowScreenQuadCB *m_shadow_mix_cb{nullptr};
};

@ -0,0 +1,67 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "shadowsScreenQuad.h"
shadowScreenQuad::shadowScreenQuad()
{
Material.Wireframe = false;
Material.Lighting = false;
video::SColor color(0x0);
Vertices[0] = video::S3DVertex(
-1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
Vertices[1] = video::S3DVertex(
-1.0f, 1.0f, 0.0f, 0, 0, 1, color, 0.0f, 0.0f);
Vertices[2] = video::S3DVertex(
1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
Vertices[3] = video::S3DVertex(
1.0f, -1.0f, 0.0f, 0, 0, 1, color, 1.0f, 1.0f);
Vertices[4] = video::S3DVertex(
-1.0f, -1.0f, 0.0f, 0, 0, 1, color, 0.0f, 1.0f);
Vertices[5] = video::S3DVertex(
1.0f, 1.0f, 0.0f, 0, 0, 1, color, 1.0f, 0.0f);
}
void shadowScreenQuad::render(video::IVideoDriver *driver)
{
u16 indices[6] = {0, 1, 2, 3, 4, 5};
driver->setMaterial(Material);
driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->drawIndexedTriangleList(&Vertices[0], 6, &indices[0], 2);
}
void shadowScreenQuadCB::OnSetConstants(
video::IMaterialRendererServices *services, s32 userData)
{
s32 TextureId = 0;
services->setPixelShaderConstant(
services->getPixelShaderConstantID("ShadowMapClientMap"),
&TextureId, 1);
TextureId = 1;
services->setPixelShaderConstant(
services->getPixelShaderConstantID("ShadowMapClientMapTraslucent"),
&TextureId, 1);
TextureId = 2;
services->setPixelShaderConstant(
services->getPixelShaderConstantID("ShadowMapSamplerdynamic"),
&TextureId, 1);
}

@ -0,0 +1,45 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h>
class shadowScreenQuad
{
public:
shadowScreenQuad();
void render(video::IVideoDriver *driver);
video::SMaterial &getMaterial() { return Material; }
private:
video::S3DVertex Vertices[6];
video::SMaterial Material;
};
class shadowScreenQuadCB : public video::IShaderConstantSetCallBack
{
public:
shadowScreenQuadCB(){};
virtual void OnSetConstants(video::IMaterialRendererServices *services,
s32 userData);
};

@ -0,0 +1,44 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "client/shadows/shadowsshadercallbacks.h"
void ShadowDepthShaderCB::OnSetConstants(
video::IMaterialRendererServices *services, s32 userData)
{
video::IVideoDriver *driver = services->getVideoDriver();
core::matrix4 lightMVP = driver->getTransform(video::ETS_PROJECTION);
lightMVP *= driver->getTransform(video::ETS_VIEW);
lightMVP *= driver->getTransform(video::ETS_WORLD);
services->setVertexShaderConstant(
services->getPixelShaderConstantID("LightMVP"),
lightMVP.pointer(), 16);
services->setVertexShaderConstant(
services->getPixelShaderConstantID("MapResolution"), &MapRes, 1);
services->setVertexShaderConstant(
services->getPixelShaderConstantID("MaxFar"), &MaxFar, 1);
s32 TextureId = 0;
services->setPixelShaderConstant(
services->getPixelShaderConstantID("ColorMapSampler"), &TextureId,
1);
}

@ -0,0 +1,34 @@
/*
Minetest
Copyright (C) 2021 Liso <anlismon@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h>
class ShadowDepthShaderCB : public video::IShaderConstantSetCallBack
{
public:
void OnSetMaterial(const video::SMaterial &material) override {}
void OnSetConstants(video::IMaterialRendererServices *services,
s32 userData) override;
f32 MaxFar{2048.0f}, MapRes{1024.0f};
};

@ -122,7 +122,14 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
m_materials[i].Lighting = true; m_materials[i].Lighting = true;
m_materials[i].MaterialType = video::EMT_SOLID; m_materials[i].MaterialType = video::EMT_SOLID;
} }
m_directional_colored_fog = g_settings->getBool("directional_colored_fog"); m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
if (g_settings->getBool("enable_dynamic_shadows")) {
float val = g_settings->getFloat("shadow_sky_body_orbit_tilt");
m_sky_body_orbit_tilt = rangelim(val, 0.0f, 60.0f);
}
setStarCount(1000, true); setStarCount(1000, true);
} }
@ -175,17 +182,7 @@ void Sky::render()
video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1); video::SColorf mooncolor_f(0.50, 0.57, 0.65, 1);
video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1); video::SColorf mooncolor2_f(0.85, 0.875, 0.9, 1);
float nightlength = 0.415; float wicked_time_of_day = getWickedTimeOfDay(m_time_of_day);
float wn = nightlength / 2;
float wicked_time_of_day = 0;
if (m_time_of_day > wn && m_time_of_day < 1.0 - wn)
wicked_time_of_day = (m_time_of_day - wn) / (1.0 - wn * 2) * 0.5 + 0.25;
else if (m_time_of_day < 0.5)
wicked_time_of_day = m_time_of_day / wn * 0.25;
else
wicked_time_of_day = 1.0 - ((1.0 - m_time_of_day) / wn * 0.25);
/*std::cerr<<"time_of_day="<<m_time_of_day<<" -> "
<<"wicked_time_of_day="<<wicked_time_of_day<<std::endl;*/
video::SColor suncolor = suncolor_f.toSColor(); video::SColor suncolor = suncolor_f.toSColor();
video::SColor suncolor2 = suncolor2_f.toSColor(); video::SColor suncolor2 = suncolor2_f.toSColor();
@ -739,10 +736,15 @@ void Sky::place_sky_body(
* day_position: turn the body around the Z axis, to place it depending of the time of the day * day_position: turn the body around the Z axis, to place it depending of the time of the day
*/ */
{ {
v3f centrum(0, 0, -1);
centrum.rotateXZBy(horizon_position);
centrum.rotateXYBy(day_position);
centrum.rotateYZBy(m_sky_body_orbit_tilt);
for (video::S3DVertex &vertex : vertices) { for (video::S3DVertex &vertex : vertices) {
// Body is directed to -Z (south) by default // Body is directed to -Z (south) by default
vertex.Pos.rotateXZBy(horizon_position); vertex.Pos.rotateXZBy(horizon_position);
vertex.Pos.rotateXYBy(day_position); vertex.Pos.rotateXYBy(day_position);
vertex.Pos.Z += centrum.Z;
} }
} }
@ -931,3 +933,17 @@ void Sky::setSkyDefaults()
m_moon_params = sky_defaults.getMoonDefaults(); m_moon_params = sky_defaults.getMoonDefaults();
m_star_params = sky_defaults.getStarDefaults(); m_star_params = sky_defaults.getStarDefaults();
} }
float getWickedTimeOfDay(float time_of_day)
{
float nightlength = 0.415f;
float wn = nightlength / 2;
float wicked_time_of_day = 0;
if (time_of_day > wn && time_of_day < 1.0f - wn)
wicked_time_of_day = (time_of_day - wn) / (1.0f - wn * 2) * 0.5f + 0.25f;
else if (time_of_day < 0.5f)
wicked_time_of_day = time_of_day / wn * 0.25f;
else
wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f);
return wicked_time_of_day;
}

@ -105,6 +105,8 @@ public:
ITextureSource *tsrc); ITextureSource *tsrc);
const video::SColorf &getCurrentStarColor() const { return m_star_color; } const video::SColorf &getCurrentStarColor() const { return m_star_color; }
float getSkyBodyOrbitTilt() const { return m_sky_body_orbit_tilt; }
private: private:
aabb3f m_box; aabb3f m_box;
video::SMaterial m_materials[SKY_MATERIAL_COUNT]; video::SMaterial m_materials[SKY_MATERIAL_COUNT];
@ -159,6 +161,7 @@ private:
bool m_directional_colored_fog; bool m_directional_colored_fog;
bool m_in_clouds = true; // Prevent duplicating bools to remember old values bool m_in_clouds = true; // Prevent duplicating bools to remember old values
bool m_enable_shaders = false; bool m_enable_shaders = false;
float m_sky_body_orbit_tilt = 0.0f;
video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
@ -205,3 +208,7 @@ private:
float horizon_position, float day_position); float horizon_position, float day_position);
void setSkyDefaults(); void setSkyDefaults();
}; };
// calculates value for sky body positions for the given observed time of day
// this is used to draw both Sun/Moon and shadows
float getWickedTimeOfDay(float time_of_day);

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h" #include "util/numeric.h"
#include <map> #include <map>
#include <IMeshManipulator.h> #include <IMeshManipulator.h>
#include "client/renderingengine.h"
#define WIELD_SCALE_FACTOR 30.0 #define WIELD_SCALE_FACTOR 30.0
#define WIELD_SCALE_FACTOR_EXTRUDED 40.0 #define WIELD_SCALE_FACTOR_EXTRUDED 40.0
@ -220,11 +221,18 @@ WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool l
m_meshnode->setReadOnlyMaterials(false); m_meshnode->setReadOnlyMaterials(false);
m_meshnode->setVisible(false); m_meshnode->setVisible(false);
dummymesh->drop(); // m_meshnode grabbed it dummymesh->drop(); // m_meshnode grabbed it
m_shadow = RenderingEngine::get_shadow_renderer();
} }
WieldMeshSceneNode::~WieldMeshSceneNode() WieldMeshSceneNode::~WieldMeshSceneNode()
{ {
sanity_check(g_extrusion_mesh_cache); sanity_check(g_extrusion_mesh_cache);
// Remove node from shadow casters
if (m_shadow)
m_shadow->removeNodeFromShadowList(m_meshnode);
if (g_extrusion_mesh_cache->drop()) if (g_extrusion_mesh_cache->drop())
g_extrusion_mesh_cache = nullptr; g_extrusion_mesh_cache = nullptr;
} }
@ -527,6 +535,10 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
// need to normalize normals when lighting is enabled (because of setScale()) // need to normalize normals when lighting is enabled (because of setScale())
m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting); m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
m_meshnode->setVisible(true); m_meshnode->setVisible(true);
// Add mesh to shadow caster
if (m_shadow)
m_shadow->addNodeToShadowList(m_meshnode);
} }
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)

@ -27,6 +27,7 @@ struct ItemStack;
class Client; class Client;
class ITextureSource; class ITextureSource;
struct ContentFeatures; struct ContentFeatures;
class ShadowRenderer;
/*! /*!
* Holds color information of an item mesh's buffer. * Holds color information of an item mesh's buffer.
@ -124,6 +125,8 @@ private:
// so this variable is just required so we can implement // so this variable is just required so we can implement
// getBoundingBox() and is set to an empty box. // getBoundingBox() and is set to an empty box.
aabb3f m_bounding_box; aabb3f m_bounding_box;
ShadowRenderer *m_shadow;
}; };
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);

@ -262,6 +262,18 @@ void set_default_settings()
settings->setDefault("enable_waving_leaves", "false"); settings->setDefault("enable_waving_leaves", "false");
settings->setDefault("enable_waving_plants", "false"); settings->setDefault("enable_waving_plants", "false");
// Effects Shadows
settings->setDefault("enable_dynamic_shadows", "false");
settings->setDefault("shadow_strength", "0.2");
settings->setDefault("shadow_map_max_distance", "200.0");
settings->setDefault("shadow_map_texture_size", "2048");
settings->setDefault("shadow_map_texture_32bit", "true");
settings->setDefault("shadow_map_color", "false");
settings->setDefault("shadow_filters", "1");
settings->setDefault("shadow_poisson_filter", "true");
settings->setDefault("shadow_update_time", "0.2");
settings->setDefault("shadow_soft_radius", "1.0");
settings->setDefault("shadow_sky_body_orbit_tilt", "0.0");
// Input // Input
settings->setDefault("invert_mouse", "false"); settings->setDefault("invert_mouse", "false");