aboutsummaryrefslogtreecommitdiff
path: root/client/shaders/object_shader/opengl_fragment.glsl
diff options
context:
space:
mode:
Diffstat (limited to 'client/shaders/object_shader/opengl_fragment.glsl')
-rw-r--r--client/shaders/object_shader/opengl_fragment.glsl373
1 files changed, 237 insertions, 136 deletions
diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl
index 3390e7227..1fefc764b 100644
--- a/client/shaders/object_shader/opengl_fragment.glsl
+++ b/client/shaders/object_shader/opengl_fragment.glsl
@@ -1,28 +1,13 @@
uniform sampler2D baseTexture;
-uniform vec4 emissiveColor;
+uniform vec3 dayLight;
uniform vec4 skyBgColor;
uniform float fogDistance;
uniform vec3 eyePosition;
-varying vec3 vNormal;
-varying vec3 vPosition;
-varying vec3 worldPosition;
-varying lowp vec4 varColor;
-#ifdef GL_ES
-varying mediump vec2 varTexCoord;
-#else
-centroid varying vec2 varTexCoord;
-#endif
-
-varying vec3 eyeVec;
-varying float vIDiff;
-
-const float e = 2.718281828459;
-const float BS = 10.0;
-const float fogStart = FOG_START;
-const float fogShadingParameter = 1.0 / (1.0 - fogStart);
-
+// 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;
@@ -31,57 +16,42 @@ 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;
+ uniform float f_shadow_strength;
+ uniform vec4 CameraPos;
+ uniform float xyPerspectiveBias0;
+ uniform float xyPerspectiveBias1;
+
varying float adj_shadow_strength;
varying float cosLight;
varying float f_normal_length;
+ varying vec3 shadow_position;
+ varying float perspective_factor;
#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);
-}
+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.
+// If the absolute position is required it can be calculated with
+// cameraOffset + worldPosition (for large coordinates the limits of float
+// precision must be considered).
+varying vec3 worldPosition;
+varying lowp vec4 varColor;
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
#endif
+varying vec3 eyeVec;
+varying float nightRatio;
-#ifdef ENABLE_DYNAMIC_SHADOWS
-const float bias0 = 0.9;
-const float zPersFactor = 0.5;
-const float bias1 = 1.0 - bias0;
+varying float vIDiff;
-vec4 getPerspectiveFactor(in vec4 shadowPosition)
-{
- float pDistance = length(shadowPosition.xy);
- float pFactor = pDistance * bias0 + bias1;
- shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPersFactor);
+const float fogStart = FOG_START;
+const float fogShadingParameter = 1.0 / (1.0 - fogStart);
- return shadowPosition;
-}
+#ifdef ENABLE_DYNAMIC_SHADOWS
// assuming near is always 1.0
float getLinearDepth()
@@ -91,11 +61,14 @@ float getLinearDepth()
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;
+ return shadow_position * 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 +97,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,32 +111,67 @@ 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
+ #define PCFBOUND 2.0 // 5x5
+ #define PCFSAMPLES 25
#elif SHADOW_FILTER == 1
- #define PCFBOUND 1.5
- #if defined(POISSON_FILTER)
- #define PCFSAMPLES 32.0
- #else
- #define PCFSAMPLES 16.0
- #endif
+ #define PCFBOUND 1.0 // 3x3
+ #define PCFSAMPLES 9
#else
#define PCFBOUND 0.0
- #if defined(POISSON_FILTER)
- #define PCFSAMPLES 4.0
- #else
- #define PCFSAMPLES 1.0
- #endif
+ #define PCFSAMPLES 1
#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
+
+#define BASEFILTERRADIUS 1.0
+
+float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
+{
+ // Return fast if sharp shadows are requested
+ if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
+ return 0.0;
+
+ vec2 clampedpos;
+ float y, x;
+ float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
+ // A factor from 0 to 1 to reduce blurring of short shadows
+ float sharpness_factor = 1.0;
+ // conversion factor from shadow depth to blur radius
+ float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
+ if (depth > 0.0 && f_normal_length > 0.0)
+ // 5 is empirical factor that controls how fast shadow loses sharpness
+ sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
+ depth = 0.0;
+
+ float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
+ * f_textureresolution / 2.0 / f_shadowfar;
+ float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
+
+ return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
+}
+
#ifdef POISSON_FILTER
const vec2[64] poissonDisk = vec2[64](
vec2(0.170019, -0.040254),
@@ -236,38 +244,54 @@ const vec2[64] poissonDisk = vec2[64](
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
+ float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
+ if (radius < 0.1) {
+ // we are in the middle of even brightness, no need for filtering
+ return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
+ }
+
vec2 clampedpos;
vec4 visibility = vec4(0.0);
+ float scale_factor = radius / f_textureresolution;
- 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 = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+ samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
+ 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] * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
}
- return visibility / PCFSAMPLES;
+ return visibility / samples;
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
+ float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
+ if (radius < 0.1) {
+ // we are in the middle of even brightness, no need for filtering
+ return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
+ }
+
vec2 clampedpos;
float visibility = 0.0;
+ float scale_factor = radius / f_textureresolution;
- 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 = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
+ samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
+ 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] * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
}
- return visibility / PCFSAMPLES;
+ return visibility / samples;
}
#endif
@@ -279,42 +303,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
+ float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
+ if (radius < 0.1) {
+ // we are in the middle of even brightness, no need for filtering
+ return getHardShadowColor(shadowsampler, smTexCoord.xy, 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;
+ float x, y;
+ float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+ bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+ float scale_factor = radius / bound / f_textureresolution;
+ float n = 0.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) * scale_factor + smTexCoord.xy;
visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
+ n += 1.0;
}
- return visibility / PCFSAMPLES;
+ return visibility / max(n, 1.0);
}
#else
float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
{
+ float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
+ if (radius < 0.1) {
+ // we are in the middle of even brightness, no need for filtering
+ return getHardShadow(shadowsampler, smTexCoord.xy, 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;
+ float x, y;
+ float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
+ bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
+ float scale_factor = radius / bound / f_textureresolution;
+ float n = 0.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) * scale_factor + smTexCoord.xy;
visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
+ n += 1.0;
}
- return visibility / PCFSAMPLES;
+ return visibility / max(n, 1.0);
}
#endif
@@ -322,12 +361,46 @@ 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;
vec2 uv = varTexCoord.st;
- vec4 base = texture2D(baseTexture, uv).rgba;
+ vec4 base = texture2D(baseTexture, uv).rgba;
// 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.
@@ -341,38 +414,65 @@ void main(void)
#endif
color = base.rgb;
- vec4 col = vec4(color.rgb, base.a);
- col.rgb *= varColor.rgb;
- col.rgb *= emissiveColor.rgb * vIDiff;
+ vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
+ col.rgb *= vIDiff;
#ifdef ENABLE_DYNAMIC_SHADOWS
- float shadow_int = 0.0;
- vec3 shadow_color = vec3(0.0, 0.0, 0.0);
- vec3 posLightSpace = getLightSpacePosition();
+ if (f_shadow_strength > 0.0) {
+ float shadow_int = 0.0;
+ vec3 shadow_color = vec3(0.0, 0.0, 0.0);
+ vec3 posLightSpace = getLightSpacePosition();
+
+ float distance_rate = (1.0 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 10.0));
+ if (max(abs(posLightSpace.x - 0.5), abs(posLightSpace.y - 0.5)) > 0.5)
+ distance_rate = 0.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 || f_normal_length < 1e-3)
+ 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 || f_normal_length < 1e-3)
+ shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
+ else
+ shadow_int = 1.0;
#endif
-
- if (f_normal_length != 0 && cosLight <= 0.001) {
- shadow_int = clamp(shadow_int + 0.5 * abs(cosLight), 0.0, 1.0);
+ shadow_int *= distance_rate;
+ shadow_int = clamp(shadow_int, 0.0, 1.0);
+
+ }
+
+ // 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(max(0.0, nightRatio), 0.6);
+
+ // Apply self-shadowing when light falls at a narrow angle to the surface
+ // Cosine of the cut-off angle.
+ const float self_shadow_cutoff_cosine = 0.14;
+ if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
+ shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
+ shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
+ }
+
+ 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
}
-
- 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
@@ -389,6 +489,7 @@ void main(void)
float clarity = clamp(fogShadingParameter
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
col = mix(skyBgColor, col, clarity);
+ col = vec4(col.rgb, base.a);
- gl_FragColor = vec4(col.rgb, base.a);
+ gl_FragColor = col;
}