diff options
Diffstat (limited to 'client/shaders/object_shader/opengl_fragment.glsl')
-rw-r--r-- | client/shaders/object_shader/opengl_fragment.glsl | 373 |
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; } |