From c47313db65f968559711ac1b505ef341a9872017 Mon Sep 17 00:00:00 2001 From: Liso Date: Sun, 6 Jun 2021 18:51:21 +0200 Subject: Shadow mapping render pass (#11244) Co-authored-by: x2048 --- client/shaders/nodes_shader/opengl_fragment.glsl | 431 ++++++++++++++++++++- client/shaders/nodes_shader/opengl_vertex.glsl | 59 ++- client/shaders/object_shader/opengl_fragment.glsl | 297 +++++++++++++- client/shaders/object_shader/opengl_vertex.glsl | 46 +++ client/shaders/shadow_shaders/pass1_fragment.glsl | 13 + .../shadow_shaders/pass1_trans_fragment.glsl | 38 ++ .../shaders/shadow_shaders/pass1_trans_vertex.glsl | 26 ++ client/shaders/shadow_shaders/pass1_vertex.glsl | 26 ++ client/shaders/shadow_shaders/pass2_fragment.glsl | 23 ++ client/shaders/shadow_shaders/pass2_vertex.glsl | 9 + 10 files changed, 956 insertions(+), 12 deletions(-) create mode 100644 client/shaders/shadow_shaders/pass1_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass1_trans_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass1_trans_vertex.glsl create mode 100644 client/shaders/shadow_shaders/pass1_vertex.glsl create mode 100644 client/shaders/shadow_shaders/pass2_fragment.glsl create mode 100644 client/shaders/shadow_shaders/pass2_vertex.glsl (limited to 'client') diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index b58095063..43a8b1f25 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -7,7 +7,22 @@ uniform vec3 eyePosition; // The cameraOffset is the current center of the visible world. uniform vec3 cameraOffset; 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; // World position in the visible world (i.e. relative to the cameraOffset.) // This can be used for many shader effects without loss of precision. @@ -22,10 +37,388 @@ varying mediump vec2 varTexCoord; centroid varying vec2 varTexCoord; #endif varying vec3 eyeVec; +varying float nightRatio; const float fogStart = FOG_START; 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 /* Hable's UC2 Tone mapping parameters @@ -58,6 +451,8 @@ vec4 applyToneMapping(vec4 color) } #endif + + void main(void) { vec3 color; @@ -74,9 +469,41 @@ void main(void) #endif color = base.rgb; - 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 col = applyToneMapping(col); #endif @@ -94,6 +521,6 @@ void main(void) - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); col = mix(skyBgColor, col, clarity); col = vec4(col.rgb, base.a); - + gl_FragColor = col; } diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 1a4840d35..d316930b2 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,5 +1,4 @@ uniform mat4 mWorld; - // Color of the light emitted by the sun. uniform vec3 dayLight; uniform vec3 eyePosition; @@ -8,6 +7,7 @@ uniform vec3 eyePosition; uniform vec3 cameraOffset; uniform float animationTimer; +varying vec3 vNormal; varying vec3 vPosition; // World position in the visible world (i.e. relative to the cameraOffset.) // This can be used for many shader effects without loss of precision. @@ -24,13 +24,38 @@ varying mediump vec2 varTexCoord; #else centroid varying vec2 varTexCoord; #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. const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; 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) { @@ -86,6 +111,9 @@ float snoise(vec3 p) #endif + + + void main(void) { varTexCoord = inTexCoord0.st; @@ -136,10 +164,9 @@ void main(void) gl_Position = mWorldViewProj * inVertexPosition; #endif - vPosition = gl_Position.xyz; - eyeVec = -(mWorldView * inVertexPosition).xyz; + vNormal = inVertexNormal; // Calculate color. // Red, green and blue components are pre-multiplied with @@ -152,7 +179,7 @@ void main(void) vec4 color = inVertexColor; #endif // 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 + nightRatio * artificialLight.rgb) * 2.0; color.a = 1.0; @@ -164,4 +191,26 @@ void main(void) 0.07 * brightness); 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 + } diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 9a81d8185..8d6f57a44 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -23,8 +23,22 @@ const float BS = 10.0; const float fogStart = FOG_START; 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 A = 0.22; B = 0.30; @@ -55,11 +69,263 @@ vec4 applyToneMapping(vec4 color) } #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) { vec3 color; vec2 uv = varTexCoord.st; - vec4 base = texture2D(baseTexture, uv).rgba; #ifdef USE_DISCARD @@ -72,13 +338,34 @@ void main(void) #endif color = base.rgb; - vec4 col = vec4(color.rgb, base.a); - col.rgb *= varColor.rgb; - 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 col = applyToneMapping(col); #endif diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index f26224e82..f135ab9dc 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -13,12 +13,37 @@ varying mediump vec2 varTexCoord; centroid varying vec2 varTexCoord; #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 float vIDiff; const float e = 2.718281828459; 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) { vec3 v = normal * normal; @@ -54,4 +79,25 @@ void main(void) #else varColor = inVertexColor; #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 } diff --git a/client/shaders/shadow_shaders/pass1_fragment.glsl b/client/shaders/shadow_shaders/pass1_fragment.glsl new file mode 100644 index 000000000..2105def96 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_fragment.glsl @@ -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); +} diff --git a/client/shaders/shadow_shaders/pass1_trans_fragment.glsl b/client/shaders/shadow_shaders/pass1_trans_fragment.glsl new file mode 100644 index 000000000..9f9e5be8c --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_trans_fragment.glsl @@ -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 +} diff --git a/client/shaders/shadow_shaders/pass1_trans_vertex.glsl b/client/shaders/shadow_shaders/pass1_trans_vertex.glsl new file mode 100644 index 000000000..ca59f2796 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_trans_vertex.glsl @@ -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; +} diff --git a/client/shaders/shadow_shaders/pass1_vertex.glsl b/client/shaders/shadow_shaders/pass1_vertex.glsl new file mode 100644 index 000000000..a6d8b3db8 --- /dev/null +++ b/client/shaders/shadow_shaders/pass1_vertex.glsl @@ -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; +} diff --git a/client/shaders/shadow_shaders/pass2_fragment.glsl b/client/shaders/shadow_shaders/pass2_fragment.glsl new file mode 100644 index 000000000..00b4f9f6c --- /dev/null +++ b/client/shaders/shadow_shaders/pass2_fragment.glsl @@ -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 + +} diff --git a/client/shaders/shadow_shaders/pass2_vertex.glsl b/client/shaders/shadow_shaders/pass2_vertex.glsl new file mode 100644 index 000000000..ac445c9c7 --- /dev/null +++ b/client/shaders/shadow_shaders/pass2_vertex.glsl @@ -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); +} -- cgit v1.2.3 From f5706d444b02ccc1fcd854968087172d50cfcca2 Mon Sep 17 00:00:00 2001 From: x2048 Date: Sun, 11 Jul 2021 17:15:19 +0200 Subject: Improve shadow rendering with non-default camera FOV (#11385) * Adjust minimum filter radius for perspective * Expand shadow frustum when camera FOV changes, reuse FOV distance adjustment from numeric.cpp * Read shadow_soft_radius setting as float * Use adaptive filter radius to accomodate for PSM distortion * Adjust filter radius for texture resolution --- client/shaders/nodes_shader/opengl_fragment.glsl | 16 +++++++----- src/client/shader.cpp | 2 +- src/client/shadows/dynamicshadows.cpp | 31 ++++++++++++++---------- src/util/numeric.cpp | 11 ++++++--- 4 files changed, 37 insertions(+), 23 deletions(-) (limited to 'client') diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 43a8b1f25..9f8a21d09 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -181,9 +181,14 @@ float getDeltaPerspectiveFactor(float l) float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier) { + float baseLength = getBaseLength(smTexCoord); + float perspectiveFactor; + // Return fast if sharp shadows are requested - if (SOFTSHADOWRADIUS <= 1.0) - return SOFTSHADOWRADIUS; + if (SOFTSHADOWRADIUS <= 1.0) { + perspectiveFactor = getDeltaPerspectiveFactor(baseLength); + return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS); + } vec2 clampedpos; float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5); @@ -192,8 +197,6 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist 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; @@ -211,9 +214,10 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist } depth = depth / n; - depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001; - return max(0.5, depth * maxRadius); + + perspectiveFactor = getDeltaPerspectiveFactor(baseLength); + return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius); } #ifdef POISSON_FILTER diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 355366bd3..0b35c37af 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -740,7 +740,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, 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"); + float shadow_soft_radius = g_settings->getFloat("shadow_soft_radius"); if (shadow_soft_radius < 1.0f) shadow_soft_radius = 1.0f; shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n"; diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index 775cdebce..17b711a61 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -33,29 +33,34 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) v3f newCenter; v3f look = cam->getDirection(); + // camera view tangents + float tanFovY = tanf(cam->getFovY() * 0.5f); + float tanFovX = tanf(cam->getFovX() * 0.5f); + + // adjusted frustum boundaries + float sfNear = shadow_frustum.zNear; + float sfFar = adjustDist(shadow_frustum.zFar, cam->getFovY()); + + // adjusted camera positions 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); + camPos += look * sfNear; + camPos2 += look * sfNear; - const v3f &viewUp = cam->getCameraNode()->getUpVector(); - // viewUp.normalize(); + // center point of light frustum + float end = sfNear + sfFar; + newCenter = camPos + look * (sfNear + 0.05f * end); + v3f world_center = camPos2 + look * (sfNear + 0.05f * end); + // Create a vector to the frustum far corner + const v3f &viewUp = cam->getCameraNode()->getUpVector(); 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; + v3f boundVec = (camPos + farCorner * sfFar) - newCenter; radius = boundVec.getLength() * 2.0f; // boundVec.getLength(); float vvolume = radius * 2.0f; diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index 99e4cfb5c..702ddce95 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -159,7 +159,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, return true; } -s16 adjustDist(s16 dist, float zoom_fov) +inline float adjustDist(float dist, float zoom_fov) { // 1.775 ~= 72 * PI / 180 * 1.4, the default FOV on the client. // The heuristic threshold for zooming is half of that. @@ -167,8 +167,13 @@ s16 adjustDist(s16 dist, float zoom_fov) if (zoom_fov < 0.001f || zoom_fov > threshold_fov) return dist; - return std::round(dist * std::cbrt((1.0f - std::cos(threshold_fov)) / - (1.0f - std::cos(zoom_fov / 2.0f)))); + return dist * std::cbrt((1.0f - std::cos(threshold_fov)) / + (1.0f - std::cos(zoom_fov / 2.0f))); +} + +s16 adjustDist(s16 dist, float zoom_fov) +{ + return std::round(adjustDist((float)dist, zoom_fov)); } void setPitchYawRollRad(core::matrix4 &m, const v3f &rot) -- cgit v1.2.3 From ff2d2a6e93d75d24b3f69f2b3690bcac6440961e Mon Sep 17 00:00:00 2001 From: x2048 Date: Sun, 25 Jul 2021 12:35:12 +0200 Subject: Add smooth light-shadow transition at noon (#11430) Node faces with normals pointing East/West (+X/-X) will transition between light and shadow at noon. This code makes the transition smooth. --- client/shaders/nodes_shader/opengl_fragment.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'client') diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 9f8a21d09..64a88ebbb 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -498,8 +498,8 @@ void main(void) } - if (f_normal_length != 0 && cosLight < 0.0) { - shadow_int = clamp(1.0-nightRatio, 0.0, 1.0); + if (f_normal_length != 0 && cosLight < 0.035) { + shadow_int = max(shadow_int, min(clamp(1.0-nightRatio, 0.0, 1.0), 1 - clamp(cosLight, 0.0, 0.035)/0.035)); } shadow_int = 1.0 - (shadow_int * f_adj_shadow_strength); -- cgit v1.2.3 From bf3acbf388406f736286d990adb5f35a9023c390 Mon Sep 17 00:00:00 2001 From: x2048 Date: Sun, 25 Jul 2021 12:36:23 +0200 Subject: Distribute shadow map update over multiple frames to reduce stutter (#11422) Reduces stutter and freezes when playing. * Maintains double SM and SM Color textures * Light frustum update triggers incremental generation of shadow map into secondary 'future' textures. * Every incremental update renders a portion of the shadow draw list (split equally). * After defined number of frames (currently, 4), 'future' and 'current' textures are swapped, and DirectionalLight 'commits' the new frustum to use when rendering shadows on screen. Co-authored-by: sfan5 --- builtin/settingtypes.txt | 10 +- client/shaders/nodes_shader/opengl_fragment.glsl | 10 +- src/client/clientmap.cpp | 25 +++- src/client/clientmap.h | 2 +- src/client/game.cpp | 17 +-- src/client/shadows/dynamicshadows.cpp | 60 +++++++-- src/client/shadows/dynamicshadows.h | 9 +- src/client/shadows/dynamicshadowsrender.cpp | 156 ++++++++++++++++++----- src/client/shadows/dynamicshadowsrender.h | 7 +- src/defaultsettings.cpp | 2 +- 10 files changed, 225 insertions(+), 73 deletions(-) (limited to 'client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 17843fac8..420e9d49c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -618,11 +618,11 @@ shadow_filters (Shadow filter quality) enum 1 0,1,2 # On true translucent nodes cast colored shadows. This is expensive. shadow_map_color (Colored shadows) bool false - -# Set the shadow update time, in seconds. -# Lower value means shadows and map updates faster, but it consumes more resources. -# Minimum value: 0.001; maximum value: 0.2 -shadow_update_time (Map update time) float 0.2 0.001 0.2 +# Spread a complete update of shadow map over given amount of frames. +# Higher values might make shadows laggy, lower values +# will consume more resources. +# Minimum value: 1; maximum value: 16 +shadow_update_frames (Map shadows update frames) int 8 1 16 # Set the soft shadow radius size. # Lower values mean sharper shadows, bigger values mean softer shadows. diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 64a88ebbb..f85ca7b48 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -197,7 +197,7 @@ float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDist float pointDepth; float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; - float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND); int n = 0; for (y = -bound; y <= bound; y += 1.0) @@ -304,7 +304,7 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance float perspectiveFactor; float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES)); + int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; @@ -334,7 +334,7 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float perspectiveFactor; float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), 1, PCFSAMPLES)); + int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; @@ -370,7 +370,7 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance float texture_size = 1.0 / (f_textureresolution * 0.5); float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); int n = 0; // basic PCF filter @@ -402,7 +402,7 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) float texture_size = 1.0 / (f_textureresolution * 0.5); float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), 0.5, PCFBOUND); + float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); int n = 0; // basic PCF filter diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 8b09eade1..77f3b9fe8 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -636,7 +636,7 @@ void ClientMap::PrintInfo(std::ostream &out) } void ClientMap::renderMapShadows(video::IVideoDriver *driver, - const video::SMaterial &material, s32 pass) + const video::SMaterial &material, s32 pass, int frame, int total_frames) { bool is_transparent_pass = pass != scene::ESNRP_SOLID; std::string prefix; @@ -650,7 +650,23 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, MeshBufListList drawbufs; + int count = 0; + int low_bound = is_transparent_pass ? 0 : m_drawlist_shadow.size() / total_frames * frame; + int high_bound = is_transparent_pass ? m_drawlist_shadow.size() : m_drawlist_shadow.size() / total_frames * (frame + 1); + + // transparent pass should be rendered in one go + if (is_transparent_pass && frame != total_frames - 1) { + return; + } + for (auto &i : m_drawlist_shadow) { + // only process specific part of the list & break early + ++count; + if (count <= low_bound) + continue; + if (count > high_bound) + break; + v3s16 block_pos = i.first; MapBlock *block = i.second; @@ -705,6 +721,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, local_material.MaterialType = material.MaterialType; local_material.BackfaceCulling = material.BackfaceCulling; local_material.FrontfaceCulling = material.FrontfaceCulling; + local_material.BlendOperation = material.BlendOperation; local_material.Lighting = false; driver->setMaterial(local_material); @@ -720,6 +737,12 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, } } + // restore the driver material state + video::SMaterial clean; + clean.BlendOperation = video::EBO_ADD; + driver->setMaterial(clean); // reset material to defaults + driver->draw3DLine(v3f(), v3f(), video::SColor(0)); + g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true)); g_profiler->avg(prefix + "vertices drawn [#]", vertex_count); g_profiler->avg(prefix + "drawcalls [#]", drawcall_count); diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 93ade4c15..97ce8d355 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -125,7 +125,7 @@ public: void renderMap(video::IVideoDriver* driver, s32 pass); void renderMapShadows(video::IVideoDriver *driver, - const video::SMaterial &material, s32 pass); + const video::SMaterial &material, s32 pass, int frame, int total_frames); int getBackgroundBrightness(float max_d, u32 daylight_factor, int oldvalue, bool *sunlight_seen_result); diff --git a/src/client/game.cpp b/src/client/game.cpp index 85dd8f4bb..6fc57c8cc 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -609,7 +609,6 @@ struct GameRunData { float jump_timer; float damage_flash; float update_draw_list_timer; - float update_shadows_timer; f32 fog_range; @@ -3881,10 +3880,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, changed much */ runData.update_draw_list_timer += dtime; - runData.update_shadows_timer += dtime; float update_draw_list_delta = 0.2f; - bool draw_list_updated = false; v3f camera_direction = camera->getDirection(); if (runData.update_draw_list_timer >= update_draw_list_delta @@ -3894,18 +3891,10 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.update_draw_list_timer = 0; client->getEnv().getClientMap().updateDrawList(); runData.update_draw_list_last_cam_dir = camera_direction; - draw_list_updated = true; } - if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) { - update_draw_list_delta = shadow->getUpdateDelta(); - - if (m_camera_offset_changed || - (runData.update_shadows_timer > update_draw_list_delta && - (!draw_list_updated || shadow->getDirectionalLightCount() == 0))) { - runData.update_shadows_timer = 0; - updateShadows(); - } + if (RenderingEngine::get_shadow_renderer()) { + updateShadows(); } m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); @@ -4062,7 +4051,7 @@ void Game::updateShadows() shadow->getDirectionalLight().setDirection(sun_pos); shadow->setTimeOfDay(in_timeofday); - shadow->getDirectionalLight().update_frustum(camera, client); + shadow->getDirectionalLight().update_frustum(camera, client, m_camera_offset_changed); } /**************************************************************************** diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index 17b711a61..0c7eea0e7 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -38,8 +38,8 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) float tanFovX = tanf(cam->getFovX() * 0.5f); // adjusted frustum boundaries - float sfNear = shadow_frustum.zNear; - float sfFar = adjustDist(shadow_frustum.zFar, cam->getFovY()); + float sfNear = future_frustum.zNear; + float sfFar = adjustDist(future_frustum.zFar, cam->getFovY()); // adjusted camera positions v3f camPos2 = cam->getPosition(); @@ -87,14 +87,15 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) v3f eye_displacement = direction * vvolume; // we must compute the viewmat with the position - the camera offset - // but the shadow_frustum position must be the actual world position + // but the future_frustum position must be the actual world position v3f eye = frustumCenter - eye_displacement; - shadow_frustum.position = world_center - eye_displacement; - shadow_frustum.length = vvolume; - shadow_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f)); - shadow_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(shadow_frustum.length, - shadow_frustum.length, -shadow_frustum.length, - shadow_frustum.length,false); + future_frustum.position = world_center - eye_displacement; + future_frustum.length = vvolume; + future_frustum.ViewMat.buildCameraLookAtMatrixLH(eye, frustumCenter, v3f(0.0f, 1.0f, 0.0f)); + future_frustum.ProjOrthMat.buildProjectionMatrixOrthoLH(future_frustum.length, + future_frustum.length, -future_frustum.length, + future_frustum.length,false); + future_frustum.camera_offset = cam->getOffset(); } DirectionalLight::DirectionalLight(const u32 shadowMapResolution, @@ -104,23 +105,44 @@ DirectionalLight::DirectionalLight(const u32 shadowMapResolution, farPlane(farValue), mapRes(shadowMapResolution), pos(position) {} -void DirectionalLight::update_frustum(const Camera *cam, Client *client) +void DirectionalLight::update_frustum(const Camera *cam, Client *client, bool force) { - should_update_map_shadow = true; + if (dirty && !force) + return; + float zNear = cam->getCameraNode()->getNearValue(); float zFar = getMaxFarValue(); /////////////////////////////////// // update splits near and fars - shadow_frustum.zNear = zNear; - shadow_frustum.zFar = zFar; + future_frustum.zNear = zNear; + future_frustum.zFar = zFar; // update shadow frustum createSplitMatrices(cam); // get the draw list for shadows client->getEnv().getClientMap().updateDrawListShadow( - getPosition(), getDirection(), shadow_frustum.length); + getPosition(), getDirection(), future_frustum.length); should_update_map_shadow = true; + dirty = true; + + // when camera offset changes, adjust the current frustum view matrix to avoid flicker + v3s16 cam_offset = cam->getOffset(); + if (cam_offset != shadow_frustum.camera_offset) { + v3f rotated_offset; + shadow_frustum.ViewMat.rotateVect(rotated_offset, intToFloat(cam_offset - shadow_frustum.camera_offset, BS)); + shadow_frustum.ViewMat.setTranslation(shadow_frustum.ViewMat.getTranslation() + rotated_offset); + shadow_frustum.camera_offset = cam_offset; + } +} + +void DirectionalLight::commitFrustum() +{ + if (!dirty) + return; + + shadow_frustum = future_frustum; + dirty = false; } void DirectionalLight::setDirection(v3f dir) @@ -144,6 +166,16 @@ const m4f &DirectionalLight::getProjectionMatrix() const return shadow_frustum.ProjOrthMat; } +const m4f &DirectionalLight::getFutureViewMatrix() const +{ + return future_frustum.ViewMat; +} + +const m4f &DirectionalLight::getFutureProjectionMatrix() const +{ + return future_frustum.ProjOrthMat; +} + m4f DirectionalLight::getViewProjMatrix() { return shadow_frustum.ProjOrthMat * shadow_frustum.ViewMat; diff --git a/src/client/shadows/dynamicshadows.h b/src/client/shadows/dynamicshadows.h index a53612f7c..d8be66be8 100644 --- a/src/client/shadows/dynamicshadows.h +++ b/src/client/shadows/dynamicshadows.h @@ -34,6 +34,7 @@ struct shadowFrustum core::matrix4 ProjOrthMat; core::matrix4 ViewMat; v3f position; + v3s16 camera_offset; }; class DirectionalLight @@ -47,7 +48,7 @@ public: //DISABLE_CLASS_COPY(DirectionalLight) - void update_frustum(const Camera *cam, Client *client); + void update_frustum(const Camera *cam, Client *client, bool force = false); // when set direction is updated to negative normalized(direction) void setDirection(v3f dir); @@ -59,6 +60,8 @@ public: /// Gets the light's matrices. const core::matrix4 &getViewMatrix() const; const core::matrix4 &getProjectionMatrix() const; + const core::matrix4 &getFutureViewMatrix() const; + const core::matrix4 &getFutureProjectionMatrix() const; core::matrix4 getViewProjMatrix(); /// Gets the light's far value. @@ -88,6 +91,8 @@ public: bool should_update_map_shadow{true}; + void commitFrustum(); + private: void createSplitMatrices(const Camera *cam); @@ -99,4 +104,6 @@ private: v3f pos; v3f direction{0}; shadowFrustum shadow_frustum; + shadowFrustum future_frustum; + bool dirty{false}; }; diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index 135c9f895..350586225 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/shader.h" #include "client/client.h" #include "client/clientmap.h" +#include "profiler.h" ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) : m_device(device), m_smgr(device->getSceneManager()), - m_driver(device->getVideoDriver()), m_client(client) + m_driver(device->getVideoDriver()), m_client(client), m_current_frame(0) { m_shadows_enabled = true; @@ -43,7 +44,7 @@ ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) : m_shadow_map_texture_32bit = g_settings->getBool("shadow_map_texture_32bit"); m_shadow_map_colored = g_settings->getBool("shadow_map_color"); m_shadow_samples = g_settings->getS32("shadow_filters"); - m_update_delta = g_settings->getFloat("shadow_update_time"); + m_map_shadow_update_frames = g_settings->getS16("shadow_update_frames"); } ShadowRenderer::~ShadowRenderer() @@ -66,6 +67,9 @@ ShadowRenderer::~ShadowRenderer() if (shadowMapClientMap) m_driver->removeTexture(shadowMapClientMap); + + if (shadowMapClientMapFuture) + m_driver->removeTexture(shadowMapClientMapFuture); } void ShadowRenderer::initialize() @@ -93,11 +97,6 @@ void ShadowRenderer::initialize() } -float ShadowRenderer::getUpdateDelta() const -{ - return m_update_delta; -} - size_t ShadowRenderer::addDirectionalLight() { m_light_list.emplace_back(m_shadow_map_texture_size, @@ -152,10 +151,9 @@ void ShadowRenderer::setClearColor(video::SColor ClearColor) m_clear_color = ClearColor; } -void ShadowRenderer::update(video::ITexture *outputTarget) +void ShadowRenderer::updateSMTextures() { if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) { - m_smgr->drawAll(); return; } @@ -174,6 +172,13 @@ void ShadowRenderer::update(video::ITexture *outputTarget) true); } + if (!shadowMapClientMapFuture && m_map_shadow_update_frames > 1) { + shadowMapClientMapFuture = getSMTexture( + std::string("shadow_clientmap_bb_") + itos(m_shadow_map_texture_size), + m_shadow_map_colored ? m_texture_format_color : m_texture_format, + true); + } + if (m_shadow_map_colored && !shadowMapTextureColors) { shadowMapTextureColors = getSMTexture( std::string("shadow_colored_") + itos(m_shadow_map_texture_size), @@ -201,7 +206,22 @@ void ShadowRenderer::update(video::ITexture *outputTarget) } if (!m_shadow_node_array.empty() && !m_light_list.empty()) { - // for every directional light: + bool reset_sm_texture = false; + + // detect if SM should be regenerated + for (DirectionalLight &light : m_light_list) { + if (light.should_update_map_shadow) { + light.should_update_map_shadow = false; + m_current_frame = 0; + reset_sm_texture = true; + } + } + + video::ITexture* shadowMapTargetTexture = shadowMapClientMapFuture; + if (shadowMapTargetTexture == nullptr) + shadowMapTargetTexture = shadowMapClientMap; + + // Update SM incrementally: for (DirectionalLight &light : m_light_list) { // Static shader values. m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size; @@ -212,22 +232,60 @@ void ShadowRenderer::update(video::ITexture *outputTarget) // Depth texture is available in irrlicth maybe we // should put some gl* fn here - if (light.should_update_map_shadow) { - light.should_update_map_shadow = false; - m_driver->setRenderTarget(shadowMapClientMap, true, true, + if (m_current_frame < m_map_shadow_update_frames) { + m_driver->setRenderTarget(shadowMapTargetTexture, reset_sm_texture, true, video::SColor(255, 255, 255, 255)); - renderShadowMap(shadowMapClientMap, light); - - if (m_shadow_map_colored) { - m_driver->setRenderTarget(shadowMapTextureColors, - true, false, video::SColor(255, 255, 255, 255)); + renderShadowMap(shadowMapTargetTexture, light); + + // Render transparent part in one pass. + // This is also handled in ClientMap. + if (m_current_frame == m_map_shadow_update_frames - 1) { + if (m_shadow_map_colored) { + m_driver->setRenderTarget(shadowMapTextureColors, + true, false, video::SColor(255, 255, 255, 255)); + } + renderShadowMap(shadowMapTextureColors, light, + scene::ESNRP_TRANSPARENT); } - renderShadowMap(shadowMapTextureColors, light, - scene::ESNRP_TRANSPARENT); m_driver->setRenderTarget(0, false, false); } + reset_sm_texture = false; + } // end for lights + + // move to the next section + if (m_current_frame <= m_map_shadow_update_frames) + ++m_current_frame; + + // pass finished, swap textures and commit light changes + if (m_current_frame == m_map_shadow_update_frames) { + if (shadowMapClientMapFuture != nullptr) + std::swap(shadowMapClientMapFuture, shadowMapClientMap); + + // Let all lights know that maps are updated + for (DirectionalLight &light : m_light_list) + light.commitFrustum(); + } + } +} + +void ShadowRenderer::update(video::ITexture *outputTarget) +{ + if (!m_shadows_enabled || m_smgr->getActiveCamera() == nullptr) { + m_smgr->drawAll(); + return; + } + + updateSMTextures(); + + if (!m_shadow_node_array.empty() && !m_light_list.empty()) { + + for (DirectionalLight &light : m_light_list) { + // Static shader values. + m_shadow_depth_cb->MapRes = (f32)m_shadow_map_texture_size; + m_shadow_depth_cb->MaxFar = (f32)m_shadow_map_max_distance * BS; + // render shadows for the n0n-map objects. m_driver->setRenderTarget(shadowMapTextureDynamicObjects, true, true, video::SColor(255, 255, 255, 255)); @@ -299,8 +357,8 @@ video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name void ShadowRenderer::renderShadowMap(video::ITexture *target, DirectionalLight &light, scene::E_SCENE_NODE_RENDER_PASS pass) { - m_driver->setTransform(video::ETS_VIEW, light.getViewMatrix()); - m_driver->setTransform(video::ETS_PROJECTION, light.getProjectionMatrix()); + m_driver->setTransform(video::ETS_VIEW, light.getFutureViewMatrix()); + m_driver->setTransform(video::ETS_PROJECTION, light.getFutureProjectionMatrix()); // Operate on the client map for (const auto &shadow_node : m_shadow_node_array) { @@ -322,10 +380,13 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target, //material.PolygonOffsetDepthBias = 1.0f/4.0f; //material.PolygonOffsetSlopeScale = -1.f; - if (m_shadow_map_colored && pass != scene::ESNRP_SOLID) + if (m_shadow_map_colored && pass != scene::ESNRP_SOLID) { material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader_trans; - else + } + else { material.MaterialType = (video::E_MATERIAL_TYPE) depth_shader; + material.BlendOperation = video::EBO_MIN; + } // FIXME: I don't think this is needed here map_node->OnAnimate(m_device->getTimer()->getTime()); @@ -333,7 +394,7 @@ void ShadowRenderer::renderShadowMap(video::ITexture *target, m_driver->setTransform(video::ETS_WORLD, map_node->getAbsoluteTransformation()); - map_node->renderMapShadows(m_driver, material, pass); + map_node->renderMapShadows(m_driver, material, pass, m_current_frame, m_map_shadow_update_frames); break; } } @@ -354,8 +415,10 @@ void ShadowRenderer::renderShadowObjects( u32 n_node_materials = shadow_node.node->getMaterialCount(); std::vector BufferMaterialList; std::vector> BufferMaterialCullingList; + std::vector BufferBlendOperationList; BufferMaterialList.reserve(n_node_materials); BufferMaterialCullingList.reserve(n_node_materials); + BufferBlendOperationList.reserve(n_node_materials); // backup materialtype for each material // (aka shader) @@ -365,12 +428,11 @@ void ShadowRenderer::renderShadowObjects( BufferMaterialList.push_back(current_mat.MaterialType); current_mat.MaterialType = - (video::E_MATERIAL_TYPE)depth_shader; - - current_mat.setTexture(3, shadowMapTextureFinal); + (video::E_MATERIAL_TYPE)depth_shader_entities; BufferMaterialCullingList.emplace_back( (bool)current_mat.BackfaceCulling, (bool)current_mat.FrontfaceCulling); + BufferBlendOperationList.push_back(current_mat.BlendOperation); current_mat.BackfaceCulling = true; current_mat.FrontfaceCulling = false; @@ -393,6 +455,7 @@ void ShadowRenderer::renderShadowObjects( current_mat.BackfaceCulling = BufferMaterialCullingList[m].first; current_mat.FrontfaceCulling = BufferMaterialCullingList[m].second; + current_mat.BlendOperation = BufferBlendOperationList[m]; } } // end for caster shadow nodes @@ -433,7 +496,7 @@ void ShadowRenderer::createShaders() readShaderFile(depth_shader_vs).c_str(), "vertexMain", video::EVST_VS_1_1, readShaderFile(depth_shader_fs).c_str(), "pixelMain", - video::EPST_PS_1_2, m_shadow_depth_cb); + video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND); if (depth_shader == -1) { // upsi, something went wrong loading shader. @@ -449,6 +512,41 @@ void ShadowRenderer::createShaders() m_driver->getMaterialRenderer(depth_shader)->grab(); } + // This creates a clone of depth_shader with base material set to EMT_SOLID, + // because entities won't render shadows with base material EMP_ONETEXTURE_BLEND + if (depth_shader_entities == -1) { + std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass1_vertex.glsl"); + if (depth_shader_vs.empty()) { + m_shadows_enabled = false; + errorstream << "Error shadow mapping vs shader not found." << std::endl; + return; + } + std::string depth_shader_fs = getShaderPath("shadow_shaders", "pass1_fragment.glsl"); + if (depth_shader_fs.empty()) { + m_shadows_enabled = false; + errorstream << "Error shadow mapping fs shader not found." << std::endl; + return; + } + + depth_shader_entities = gpu->addHighLevelShaderMaterial( + readShaderFile(depth_shader_vs).c_str(), "vertexMain", + video::EVST_VS_1_1, + readShaderFile(depth_shader_fs).c_str(), "pixelMain", + video::EPST_PS_1_2, m_shadow_depth_cb); + + if (depth_shader_entities == -1) { + // upsi, something went wrong loading shader. + m_shadows_enabled = false; + errorstream << "Error compiling shadow mapping shader (dynamic)." << std::endl; + return; + } + + // HACK, TODO: investigate this better + // Grab the material renderer once more so minetest doesn't crash + // on exit + m_driver->getMaterialRenderer(depth_shader_entities)->grab(); + } + if (mixcsm_shader == -1) { std::string depth_shader_vs = getShaderPath("shadow_shaders", "pass2_vertex.glsl"); if (depth_shader_vs.empty()) { diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h index e633bd4f7..52b24a18f 100644 --- a/src/client/shadows/dynamicshadowsrender.h +++ b/src/client/shadows/dynamicshadowsrender.h @@ -64,7 +64,6 @@ public: size_t getDirectionalLightCount() const; f32 getMaxShadowFar() const; - float getUpdateDelta() const; /// Adds a shadow to the scene node. /// The shadow mode can be ESM_BOTH, or ESM_RECEIVE. /// ESM_BOTH casts and receives shadows @@ -101,6 +100,7 @@ private: scene::ESNRP_SOLID); void renderShadowObjects(video::ITexture *target, DirectionalLight &light); void mixShadowsQuad(); + void updateSMTextures(); // a bunch of variables IrrlichtDevice *m_device{nullptr}; @@ -108,6 +108,7 @@ private: video::IVideoDriver *m_driver{nullptr}; Client *m_client{nullptr}; video::ITexture *shadowMapClientMap{nullptr}; + video::ITexture *shadowMapClientMapFuture{nullptr}; video::ITexture *shadowMapTextureFinal{nullptr}; video::ITexture *shadowMapTextureDynamicObjects{nullptr}; video::ITexture *shadowMapTextureColors{nullptr}; @@ -120,11 +121,12 @@ private: float m_shadow_map_max_distance; float m_shadow_map_texture_size; float m_time_day{0.0f}; - float m_update_delta; int m_shadow_samples; bool m_shadow_map_texture_32bit; bool m_shadows_enabled; bool m_shadow_map_colored; + u8 m_map_shadow_update_frames; /* Use this number of frames to update map shaodw */ + u8 m_current_frame{0}; /* Current frame */ video::ECOLOR_FORMAT m_texture_format{video::ECOLOR_FORMAT::ECF_R16F}; video::ECOLOR_FORMAT m_texture_format_color{video::ECOLOR_FORMAT::ECF_R16G16}; @@ -135,6 +137,7 @@ private: std::string readShaderFile(const std::string &path); s32 depth_shader{-1}; + s32 depth_shader_entities{-1}; s32 depth_shader_trans{-1}; s32 mixcsm_shader{-1}; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6791fccf5..faf839b3a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -272,7 +272,7 @@ void set_default_settings() 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_update_frames", "8"); settings->setDefault("shadow_soft_radius", "1.0"); settings->setDefault("shadow_sky_body_orbit_tilt", "0.0"); -- cgit v1.2.3 From ea250ff5c57301b6ea3e529c811484c743c1fde1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Sep 2021 21:59:29 +0200 Subject: Fix GLES2 discard behaviour (texture transparency) --- client/shaders/nodes_shader/opengl_fragment.glsl | 9 ++++++--- client/shaders/object_shader/opengl_fragment.glsl | 9 ++++++--- src/client/shader.cpp | 8 ++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'client') diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index f85ca7b48..87ef9af7d 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -463,13 +463,16 @@ void main(void) vec2 uv = varTexCoord.st; vec4 base = texture2D(baseTexture, uv).rgba; -#ifdef USE_DISCARD // If alpha is zero, we can just discard the pixel. This fixes transparency // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa, // and also on GLES 2, where GL_ALPHA_TEST is missing entirely. - if (base.a == 0.0) { +#ifdef USE_DISCARD + if (base.a == 0.0) + discard; +#endif +#ifdef USE_DISCARD_REF + if (base.a < 0.5) discard; - } #endif color = base.rgb; diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 8d6f57a44..9a0b90f15 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -328,13 +328,16 @@ void main(void) vec2 uv = varTexCoord.st; vec4 base = texture2D(baseTexture, uv).rgba; -#ifdef USE_DISCARD // If alpha is zero, we can just discard the pixel. This fixes transparency // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa, // and also on GLES 2, where GL_ALPHA_TEST is missing entirely. - if (base.a == 0.0) { +#ifdef USE_DISCARD + if (base.a == 0.0) + discard; +#endif +#ifdef USE_DISCARD_REF + if (base.a < 0.5) discard; - } #endif color = base.rgb; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 0b35c37af..dc9e9ae6d 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -674,8 +674,12 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, if (strstr(gl_renderer, "GC7000")) use_discard = true; #endif - if (use_discard && shaderinfo.base_material != video::EMT_SOLID) - shaders_header << "#define USE_DISCARD 1\n"; + if (use_discard) { + if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL) + shaders_header << "#define USE_DISCARD 1\n"; + else if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF) + shaders_header << "#define USE_DISCARD_REF 1\n"; + } #define PROVIDE(constant) shaders_header << "#define " #constant " " << (int)constant << "\n" -- cgit v1.2.3