]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Copy shadow mapping shader from nodes to objects
authorDmitry Kostenko <codeforsmile@gmail.com>
Wed, 3 Nov 2021 22:39:30 +0000 (23:39 +0100)
committerx2048 <codeforsmile@gmail.com>
Mon, 7 Mar 2022 22:45:26 +0000 (23:45 +0100)
client/shaders/object_shader/opengl_fragment.glsl
client/shaders/object_shader/opengl_vertex.glsl

index 3390e72277e21ba169c61012f9115ce9c525f67b..0b9dbc996817fd59316905db50b742514e264ef2 100644 (file)
@@ -1,6 +1,7 @@
 uniform sampler2D baseTexture;
 
 uniform vec4 emissiveColor;
+uniform vec3 dayLight;
 uniform vec4 skyBgColor;
 uniform float fogDistance;
 uniform vec3 eyePosition;
@@ -16,6 +17,8 @@ centroid varying vec2 varTexCoord;
 #endif
 
 varying vec3 eyeVec;
+varying float nightRatio;
+
 varying float vIDiff;
 
 const float e = 2.718281828459;
@@ -31,53 +34,23 @@ const float fogShadingParameter = 1.0 / (1.0 - fogStart);
        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;
-       C = 0.10;
-       D = 0.20;
-       E = 0.01;
-       F = 0.30;
-       W = 11.2;
-       equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
-*/
-
-vec3 uncharted2Tonemap(vec3 x)
-{
-       return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
-}
-
-vec4 applyToneMapping(vec4 color)
-{
-       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
-       const float gamma = 1.6;
-       const float exposureBias = 5.5;
-       color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
-       // Precalculated white_scale from
-       //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
-       vec3 whiteScale = vec3(1.036015346);
-       color.rgb *= whiteScale;
-       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
-}
-#endif
-
 #ifdef ENABLE_DYNAMIC_SHADOWS
 const float bias0 = 0.9;
 const float zPersFactor = 0.5;
-const float bias1 = 1.0 - bias0;
+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;
@@ -92,11 +65,23 @@ float getLinearDepth()
 vec3 getLightSpacePosition()
 {
        vec4 pLightSpace;
-       float normalBias = 0.0005 * getLinearDepth() * cosLight + normalOffsetScale;
-       pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalBias * normalize(vNormal), 1.0);
+       // 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
 
@@ -124,10 +109,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
 {
        vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
 
-       float visibility = step(0.0, (realDistance-2e-5) - texDepth.r);
+       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-2e-5) - texDepth.r);
+               visibility = step(0.0, realDistance - texDepth.b);
                result = vec4(visibility, unpackColor(texDepth.a));
        }
        return result;
@@ -138,13 +123,13 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
 float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 {
        float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
-       float visibility = step(0.0, (realDistance-2e-5) - texDepth);
-
+       float visibility = step(0.0, realDistance - texDepth);
        return visibility;
 }
 
 #endif
 
+
 #if SHADOW_FILTER == 2
        #define PCFBOUND 3.5
        #define PCFSAMPLES 64.0
@@ -163,6 +148,73 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance
                #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)
+{
+       float baseLength = getBaseLength(smTexCoord);
+       float perspectiveFactor;
+
+       // Return fast if sharp shadows are requested
+       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);
+       float y, x;
+       float depth = 0.0;
+       float pointDepth;
+       float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
+
+       float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, 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;
+
+       perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
+       return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
+}
 
 #ifdef POISSON_FILTER
 const vec2[64] poissonDisk = vec2[64](
@@ -238,17 +290,28 @@ 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
-       int end_offset = int(PCFSAMPLES) + init_offset;
+       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;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
+               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 / PCFSAMPLES;
+       return visibility / samples;
 }
 
 #else
@@ -257,17 +320,28 @@ 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 init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-PCFSAMPLES)));
-       int end_offset = int(PCFSAMPLES) + init_offset;
+       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;
 
        for (int x = init_offset; x < end_offset; x++) {
-               clampedpos = poissonDisk[x] * texture_size * SOFTSHADOWRADIUS + smTexCoord.xy;
+               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 / PCFSAMPLES;
+       return visibility / samples;
 }
 
 #endif
@@ -281,19 +355,31 @@ 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 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), PCFBOUND / 2, PCFBOUND);
+       int n = 0;
+
        // 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;
+       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 / PCFSAMPLES;
+       return visibility / n;
 }
 
 #else
@@ -301,20 +387,31 @@ 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 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), PCFBOUND / 2, PCFBOUND);
+       int n = 0;
+
        // 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;
+       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 / PCFSAMPLES;
+       return visibility / n;
 }
 
 #endif
@@ -322,6 +419,37 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 #endif
 #endif
 
+#if ENABLE_TONE_MAPPING
+/* Hable's UC2 Tone mapping parameters
+       A = 0.22;
+       B = 0.30;
+       C = 0.10;
+       D = 0.20;
+       E = 0.01;
+       F = 0.30;
+       W = 11.2;
+       equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
+*/
+
+vec3 uncharted2Tonemap(vec3 x)
+{
+       return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
+}
+
+vec4 applyToneMapping(vec4 color)
+{
+       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
+       const float gamma = 1.6;
+       const float exposureBias = 5.5;
+       color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
+       // Precalculated white_scale from
+       //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
+       vec3 whiteScale = vec3(1.036015346);
+       color.rgb *= whiteScale;
+       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
+}
+#endif
+
 void main(void)
 {
        vec3 color;
@@ -350,28 +478,50 @@ void main(void)
        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;
-       if (cosLight > 0.0)
-               visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
-       else
-               visibility = vec4(1.0, 0.0, 0.0, 0.0);
-       shadow_int = visibility.r;
-       shadow_color = visibility.gba;
+               vec4 visibility;
+               if (cosLight > 0.0)
+                       visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
+               else
+                       visibility = vec4(1.0, 0.0, 0.0, 0.0);
+               shadow_int = visibility.r;
+               shadow_color = visibility.gba;
 #else
-       shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
+               if (cosLight > 0.0)
+                       shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
+               else
+                       shadow_int = 1.0;
 #endif
+               shadow_int *= distance_rate;
+               shadow_int = clamp(shadow_int, 0.0, 1.0);
 
-       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
+       // turns out that nightRatio falls off much faster than
+       // actual brightness of artificial light in relation to natual light.
+       // Power ratio was measured on torches in MTG (brightness = 14).
+       float adjusted_night_ratio = pow(nightRatio, 0.6);
 
+       if (f_normal_length != 0 && cosLight < 0.035) {
+               shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, 0.035)/0.035);
+       }
 
+       shadow_int *= f_adj_shadow_strength;
+       
+       // calculate fragment color from components:
+       col.rgb =
+                       adjusted_night_ratio * col.rgb + // artificial light
+                       (1.0 - adjusted_night_ratio) * ( // natural light
+                                       col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) +  // filtered texture color
+                                       dayLight * shadow_color * shadow_int);                 // reflected filtered sunlight/moonlight
+       // col.r = 0.5 * clamp(getPenumbraRadius(ShadowMapSampler, posLightSpace.xy, posLightSpace.z, 1.0) / SOFTSHADOWRADIUS, 0.0, 1.0) + 0.5 * col.r;
+       // col.r = adjusted_night_ratio; // debug night ratio adjustment
+#endif
 
 #if ENABLE_TONE_MAPPING
        col = applyToneMapping(col);
index f135ab9dc6919f42236f1b0b5e06e0afe782f225..922fba62bd19bc4ef6c377080ffcae19f3d4d35c 100644 (file)
@@ -28,6 +28,8 @@ centroid varying vec2 varTexCoord;
 #endif
 
 varying vec3 eyeVec;
+varying float nightRatio;
+
 varying float vIDiff;
 
 const float e = 2.718281828459;
@@ -60,7 +62,7 @@ void main(void)
        gl_Position = mWorldViewProj * inVertexPosition;
 
        vPosition = gl_Position.xyz;
-       vNormal = inVertexNormal;
+       vNormal = (mWorld * vec4(inVertexNormal, 0.0)).xyz;
        worldPosition = (mWorld * inVertexPosition).xyz;
        eyeVec = -(mWorldView * inVertexPosition).xyz;
 
@@ -73,6 +75,7 @@ void main(void)
                ? 1.0
                : directional_ambient(normalize(inVertexNormal));
 #endif
+       nightRatio = 0.0;
 
 #ifdef GL_ES
        varColor = inVertexColor.bgra;
@@ -81,11 +84,12 @@ void main(void)
 #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);
+       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));
@@ -98,6 +102,5 @@ void main(void)
                        (1.0 - mtsmoothstep(0.7, 0.8, f_timeofday));
        }
        f_normal_length = length(vNormal);
-
 #endif
 }