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