diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 3390e7227..0b9dbc996 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -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 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); + 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); } - return visibility / PCFSAMPLES; + 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 = -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 @@ -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 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); + 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); } - return visibility / PCFSAMPLES; + 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 = -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 @@ -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,29 +478,51 @@ void main(void) vec3 shadow_color = vec3(0.0, 0.0, 0.0); vec3 posLightSpace = getLightSpacePosition(); -#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; -#else - shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); -#endif + 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; +#else + 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); + // 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); - col.rgb = mix(shadow_color, col.rgb, shadow_int) * shadow_int; + 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); #endif diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index f135ab9dc..922fba62b 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -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 }