]> git.lizzy.rs Git - dragonfireclient.git/blob - client/shaders/nodes_shader/opengl_fragment.glsl
Tune shadow perspective distortion (#12146)
[dragonfireclient.git] / client / shaders / nodes_shader / opengl_fragment.glsl
1 uniform sampler2D baseTexture;
2
3 uniform vec3 dayLight;
4 uniform vec4 skyBgColor;
5 uniform float fogDistance;
6 uniform vec3 eyePosition;
7
8 // The cameraOffset is the current center of the visible world.
9 uniform vec3 cameraOffset;
10 uniform float animationTimer;
11 #ifdef ENABLE_DYNAMIC_SHADOWS
12         // shadow texture
13         uniform sampler2D ShadowMapSampler;
14         // shadow uniforms
15         uniform vec3 v_LightDirection;
16         uniform float f_textureresolution;
17         uniform mat4 m_ShadowViewProj;
18         uniform float f_shadowfar;
19         uniform float f_shadow_strength;
20         varying float normalOffsetScale;
21         varying float adj_shadow_strength;
22         varying float cosLight;
23         varying float f_normal_length;
24 #endif
25
26
27 varying vec3 vNormal;
28 varying vec3 vPosition;
29 // World position in the visible world (i.e. relative to the cameraOffset.)
30 // This can be used for many shader effects without loss of precision.
31 // If the absolute position is required it can be calculated with
32 // cameraOffset + worldPosition (for large coordinates the limits of float
33 // precision must be considered).
34 varying vec3 worldPosition;
35 varying lowp vec4 varColor;
36 #ifdef GL_ES
37 varying mediump vec2 varTexCoord;
38 #else
39 centroid varying vec2 varTexCoord;
40 #endif
41 varying vec3 eyeVec;
42 varying float nightRatio;
43
44 const float fogStart = FOG_START;
45 const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
46
47
48
49 #ifdef ENABLE_DYNAMIC_SHADOWS
50 uniform float xyPerspectiveBias0;
51 uniform float xyPerspectiveBias1;
52 uniform float zPerspectiveBias;
53
54 vec4 getPerspectiveFactor(in vec4 shadowPosition)
55 {
56
57         float pDistance = length(shadowPosition.xy);
58         float pFactor = pDistance * xyPerspectiveBias0 + xyPerspectiveBias1;
59
60         shadowPosition.xyz *= vec3(vec2(1.0 / pFactor), zPerspectiveBias);
61
62         return shadowPosition;
63 }
64
65 // assuming near is always 1.0
66 float getLinearDepth()
67 {
68         return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
69 }
70
71 vec3 getLightSpacePosition()
72 {
73         vec4 pLightSpace;
74         // some drawtypes have zero normals, so we need to handle it :(
75         #if DRAW_TYPE == NDT_PLANTLIKE
76         pLightSpace = m_ShadowViewProj * vec4(worldPosition, 1.0);
77         #else
78         pLightSpace = m_ShadowViewProj * vec4(worldPosition + normalOffsetScale * normalize(vNormal), 1.0);
79         #endif
80         pLightSpace = getPerspectiveFactor(pLightSpace);
81         return pLightSpace.xyz * 0.5 + 0.5;
82 }
83 // custom smoothstep implementation because it's not defined in glsl1.2
84 // https://docs.gl/sl4/smoothstep
85 float mtsmoothstep(in float edge0, in float edge1, in float x)
86 {
87         float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
88         return t * t * (3.0 - 2.0 * t);
89 }
90
91 #ifdef COLORED_SHADOWS
92
93 // c_precision of 128 fits within 7 base-10 digits
94 const float c_precision = 128.0;
95 const float c_precisionp1 = c_precision + 1.0;
96
97 float packColor(vec3 color)
98 {
99         return floor(color.b * c_precision + 0.5)
100                 + floor(color.g * c_precision + 0.5) * c_precisionp1
101                 + floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
102 }
103
104 vec3 unpackColor(float value)
105 {
106         vec3 color;
107         color.b = mod(value, c_precisionp1) / c_precision;
108         color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
109         color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
110         return color;
111 }
112
113 vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
114 {
115         vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
116
117         float visibility = step(0.0, realDistance - texDepth.r);
118         vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
119         if (visibility < 0.1) {
120                 visibility = step(0.0, realDistance - texDepth.b);
121                 result = vec4(visibility, unpackColor(texDepth.a));
122         }
123         return result;
124 }
125
126 #else
127
128 float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
129 {
130         float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
131         float visibility = step(0.0, realDistance - texDepth);
132         return visibility;
133 }
134
135 #endif
136
137
138 #if SHADOW_FILTER == 2
139         #define PCFBOUND 3.5
140         #define PCFSAMPLES 64.0
141 #elif SHADOW_FILTER == 1
142         #define PCFBOUND 1.5
143         #if defined(POISSON_FILTER)
144                 #define PCFSAMPLES 32.0
145         #else
146                 #define PCFSAMPLES 16.0
147         #endif
148 #else
149         #define PCFBOUND 0.0
150         #if defined(POISSON_FILTER)
151                 #define PCFSAMPLES 4.0
152         #else
153                 #define PCFSAMPLES 1.0
154         #endif
155 #endif
156 #ifdef COLORED_SHADOWS
157 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
158 {
159         vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
160         float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
161         return depth;
162 }
163 #else
164 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
165 {
166         float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
167         float depth = realDistance - texDepth;
168         return depth;
169 }
170 #endif
171
172 float getBaseLength(vec2 smTexCoord)
173 {
174         float l = length(2.0 * smTexCoord.xy - 1.0);     // length in texture coords
175         return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0);                              // return to undistorted coords
176 }
177
178 float getDeltaPerspectiveFactor(float l)
179 {
180         return 0.1 / (xyPerspectiveBias0 * l + xyPerspectiveBias1);                      // original distortion factor, divided by 10
181 }
182
183 float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier)
184 {
185         float baseLength = getBaseLength(smTexCoord);
186         float perspectiveFactor;
187
188         if (PCFBOUND == 0.0) return 0.0;
189         // Return fast if sharp shadows are requested
190         if (PCFBOUND == 0.0)
191                 return 0.0;
192
193         if (SOFTSHADOWRADIUS <= 1.0) {
194                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
195                 return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS);
196         }
197
198         vec2 clampedpos;
199         float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5);
200         float y, x;
201         float depth = 0.0;
202         float pointDepth;
203         float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier;
204
205         float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND);
206         int n = 0;
207
208         for (y = -bound; y <= bound; y += 1.0)
209         for (x = -bound; x <= bound; x += 1.0) {
210                 clampedpos = vec2(x,y);
211                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius);
212                 clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy;
213
214                 pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance);
215                 if (pointDepth > -0.01) {
216                         depth += pointDepth;
217                         n += 1;
218                 }
219         }
220
221         depth = depth / n;
222         depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001;
223
224         perspectiveFactor = getDeltaPerspectiveFactor(baseLength);
225         return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius);
226 }
227
228 #ifdef POISSON_FILTER
229 const vec2[64] poissonDisk = vec2[64](
230         vec2(0.170019, -0.040254),
231         vec2(-0.299417, 0.791925),
232         vec2(0.645680, 0.493210),
233         vec2(-0.651784, 0.717887),
234         vec2(0.421003, 0.027070),
235         vec2(-0.817194, -0.271096),
236         vec2(-0.705374, -0.668203),
237         vec2(0.977050, -0.108615),
238         vec2(0.063326, 0.142369),
239         vec2(0.203528, 0.214331),
240         vec2(-0.667531, 0.326090),
241         vec2(-0.098422, -0.295755),
242         vec2(-0.885922, 0.215369),
243         vec2(0.566637, 0.605213),
244         vec2(0.039766, -0.396100),
245         vec2(0.751946, 0.453352),
246         vec2(0.078707, -0.715323),
247         vec2(-0.075838, -0.529344),
248         vec2(0.724479, -0.580798),
249         vec2(0.222999, -0.215125),
250         vec2(-0.467574, -0.405438),
251         vec2(-0.248268, -0.814753),
252         vec2(0.354411, -0.887570),
253         vec2(0.175817, 0.382366),
254         vec2(0.487472, -0.063082),
255         vec2(0.355476, 0.025357),
256         vec2(-0.084078, 0.898312),
257         vec2(0.488876, -0.783441),
258         vec2(0.470016, 0.217933),
259         vec2(-0.696890, -0.549791),
260         vec2(-0.149693, 0.605762),
261         vec2(0.034211, 0.979980),
262         vec2(0.503098, -0.308878),
263         vec2(-0.016205, -0.872921),
264         vec2(0.385784, -0.393902),
265         vec2(-0.146886, -0.859249),
266         vec2(0.643361, 0.164098),
267         vec2(0.634388, -0.049471),
268         vec2(-0.688894, 0.007843),
269         vec2(0.464034, -0.188818),
270         vec2(-0.440840, 0.137486),
271         vec2(0.364483, 0.511704),
272         vec2(0.034028, 0.325968),
273         vec2(0.099094, -0.308023),
274         vec2(0.693960, -0.366253),
275         vec2(0.678884, -0.204688),
276         vec2(0.001801, 0.780328),
277         vec2(0.145177, -0.898984),
278         vec2(0.062655, -0.611866),
279         vec2(0.315226, -0.604297),
280         vec2(-0.780145, 0.486251),
281         vec2(-0.371868, 0.882138),
282         vec2(0.200476, 0.494430),
283         vec2(-0.494552, -0.711051),
284         vec2(0.612476, 0.705252),
285         vec2(-0.578845, -0.768792),
286         vec2(-0.772454, -0.090976),
287         vec2(0.504440, 0.372295),
288         vec2(0.155736, 0.065157),
289         vec2(0.391522, 0.849605),
290         vec2(-0.620106, -0.328104),
291         vec2(0.789239, -0.419965),
292         vec2(-0.545396, 0.538133),
293         vec2(-0.178564, -0.596057)
294 );
295
296 #ifdef COLORED_SHADOWS
297
298 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
299 {
300         vec2 clampedpos;
301         vec4 visibility = vec4(0.0);
302         float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
303         if (radius < 0.1) {
304                 // we are in the middle of even brightness, no need for filtering
305                 return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
306         }
307
308         float baseLength = getBaseLength(smTexCoord);
309         float perspectiveFactor;
310
311         float texture_size = 1.0 / (f_textureresolution * 0.5);
312         int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
313         int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
314         int end_offset = int(samples) + init_offset;
315
316         for (int x = init_offset; x < end_offset; x++) {
317                 clampedpos = poissonDisk[x];
318                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
319                 clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
320                 visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
321         }
322
323         return visibility / samples;
324 }
325
326 #else
327
328 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
329 {
330         vec2 clampedpos;
331         float visibility = 0.0;
332         float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF
333         if (radius < 0.1) {
334                 // we are in the middle of even brightness, no need for filtering
335                 return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
336         }
337
338         float baseLength = getBaseLength(smTexCoord);
339         float perspectiveFactor;
340
341         float texture_size = 1.0 / (f_textureresolution * 0.5);
342         int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES));
343         int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
344         int end_offset = int(samples) + init_offset;
345
346         for (int x = init_offset; x < end_offset; x++) {
347                 clampedpos = poissonDisk[x];
348                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius);
349                 clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy;
350                 visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
351         }
352
353         return visibility / samples;
354 }
355
356 #endif
357
358 #else
359 /* poisson filter disabled */
360
361 #ifdef COLORED_SHADOWS
362
363 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
364 {
365         vec2 clampedpos;
366         vec4 visibility = vec4(0.0);
367         float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
368         if (radius < 0.1) {
369                 // we are in the middle of even brightness, no need for filtering
370                 return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
371         }
372
373         float baseLength = getBaseLength(smTexCoord);
374         float perspectiveFactor;
375
376         float texture_size = 1.0 / (f_textureresolution * 0.5);
377         float y, x;
378         float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
379         int n = 0;
380
381         // basic PCF filter
382         for (y = -bound; y <= bound; y += 1.0)
383         for (x = -bound; x <= bound; x += 1.0) {
384                 clampedpos = vec2(x,y);     // screen offset
385                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
386                 clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
387                 visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
388                 n += 1;
389         }
390
391         return visibility / n;
392 }
393
394 #else
395 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
396 {
397         vec2 clampedpos;
398         float visibility = 0.0;
399         float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0);
400         if (radius < 0.1) {
401                 // we are in the middle of even brightness, no need for filtering
402                 return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
403         }
404
405         float baseLength = getBaseLength(smTexCoord);
406         float perspectiveFactor;
407
408         float texture_size = 1.0 / (f_textureresolution * 0.5);
409         float y, x;
410         float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND);
411         int n = 0;
412
413         // basic PCF filter
414         for (y = -bound; y <= bound; y += 1.0)
415         for (x = -bound; x <= bound; x += 1.0) {
416                 clampedpos = vec2(x,y);     // screen offset
417                 perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound);
418                 clampedpos =  clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted
419                 visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
420                 n += 1;
421         }
422
423         return visibility / n;
424 }
425
426 #endif
427
428 #endif
429 #endif
430
431 #if ENABLE_TONE_MAPPING
432
433 /* Hable's UC2 Tone mapping parameters
434         A = 0.22;
435         B = 0.30;
436         C = 0.10;
437         D = 0.20;
438         E = 0.01;
439         F = 0.30;
440         W = 11.2;
441         equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
442 */
443
444 vec3 uncharted2Tonemap(vec3 x)
445 {
446         return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
447 }
448
449 vec4 applyToneMapping(vec4 color)
450 {
451         color = vec4(pow(color.rgb, vec3(2.2)), color.a);
452         const float gamma = 1.6;
453         const float exposureBias = 5.5;
454         color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
455         // Precalculated white_scale from
456         //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
457         vec3 whiteScale = vec3(1.036015346);
458         color.rgb *= whiteScale;
459         return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
460 }
461 #endif
462
463
464
465 void main(void)
466 {
467         vec3 color;
468         vec2 uv = varTexCoord.st;
469
470         vec4 base = texture2D(baseTexture, uv).rgba;
471         // If alpha is zero, we can just discard the pixel. This fixes transparency
472         // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
473         // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
474 #ifdef USE_DISCARD
475         if (base.a == 0.0)
476                 discard;
477 #endif
478 #ifdef USE_DISCARD_REF
479         if (base.a < 0.5)
480                 discard;
481 #endif
482
483         color = base.rgb;
484         vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
485
486 #ifdef ENABLE_DYNAMIC_SHADOWS
487         if (f_shadow_strength > 0.0) {
488                 float shadow_int = 0.0;
489                 vec3 shadow_color = vec3(0.0, 0.0, 0.0);
490                 vec3 posLightSpace = getLightSpacePosition();
491
492                 float distance_rate = (1 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 50.0));
493                 float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1,  posLightSpace.z),0.0);
494
495                 if (distance_rate > 1e-7) {
496                 
497 #ifdef COLORED_SHADOWS
498                         vec4 visibility;
499                         if (cosLight > 0.0)
500                                 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
501                         else
502                                 visibility = vec4(1.0, 0.0, 0.0, 0.0);
503                         shadow_int = visibility.r;
504                         shadow_color = visibility.gba;
505 #else
506                         if (cosLight > 0.0)
507                                 shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
508                         else
509                                 shadow_int = 1.0;
510 #endif
511                         shadow_int *= distance_rate;
512                         shadow_int = clamp(shadow_int, 0.0, 1.0);
513
514                 }
515
516                 // turns out that nightRatio falls off much faster than
517                 // actual brightness of artificial light in relation to natual light.
518                 // Power ratio was measured on torches in MTG (brightness = 14).
519                 float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);
520
521                 // Apply self-shadowing when light falls at a narrow angle to the surface
522                 // Cosine of the cut-off angle.
523                 const float self_shadow_cutoff_cosine = 0.035;
524                 if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
525                         shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
526                         shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
527                 }
528
529                 shadow_int *= f_adj_shadow_strength;
530                 
531                 // calculate fragment color from components:
532                 col.rgb =
533                                 adjusted_night_ratio * col.rgb + // artificial light
534                                 (1.0 - adjusted_night_ratio) * ( // natural light
535                                                 col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) +  // filtered texture color
536                                                 dayLight * shadow_color * shadow_int);                 // reflected filtered sunlight/moonlight
537         }
538 #endif
539
540 #if ENABLE_TONE_MAPPING
541         col = applyToneMapping(col);
542 #endif
543
544         // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?),
545         // the fog will only be rendered correctly if the last operation before the
546         // clamp() is an addition. Else, the clamp() seems to be ignored.
547         // E.g. the following won't work:
548         //      float clarity = clamp(fogShadingParameter
549         //              * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0);
550         // As additions usually come for free following a multiplication, the new formula
551         // should be more efficient as well.
552         // Note: clarity = (1 - fogginess)
553         float clarity = clamp(fogShadingParameter
554                 - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
555         col = mix(skyBgColor, col, clarity);
556         col = vec4(col.rgb, base.a);
557         
558         gl_FragColor = col;
559 }