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