1 uniform sampler2D baseTexture;
4 uniform vec4 skyBgColor;
5 uniform float fogDistance;
6 uniform vec3 eyePosition;
8 // The cameraOffset is the current center of the visible world.
9 uniform vec3 cameraOffset;
10 uniform float animationTimer;
11 #ifdef ENABLE_DYNAMIC_SHADOWS
13 uniform sampler2D ShadowMapSampler;
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 uniform vec4 CameraPos;
21 uniform float xyPerspectiveBias0;
22 uniform float xyPerspectiveBias1;
24 varying float adj_shadow_strength;
25 varying float cosLight;
26 varying float f_normal_length;
27 varying vec3 shadow_position;
28 varying float perspective_factor;
33 varying vec3 vPosition;
34 // World position in the visible world (i.e. relative to the cameraOffset.)
35 // This can be used for many shader effects without loss of precision.
36 // If the absolute position is required it can be calculated with
37 // cameraOffset + worldPosition (for large coordinates the limits of float
38 // precision must be considered).
39 varying vec3 worldPosition;
40 varying lowp vec4 varColor;
42 varying mediump vec2 varTexCoord;
44 centroid varying vec2 varTexCoord;
47 varying float nightRatio;
51 const float fogStart = FOG_START;
52 const float fogShadingParameter = 1.0 / (1.0 - fogStart);
54 #ifdef ENABLE_DYNAMIC_SHADOWS
56 // assuming near is always 1.0
57 float getLinearDepth()
59 return 2.0 * f_shadowfar / (f_shadowfar + 1.0 - (2.0 * gl_FragCoord.z - 1.0) * (f_shadowfar - 1.0));
62 vec3 getLightSpacePosition()
64 return shadow_position * 0.5 + 0.5;
66 // custom smoothstep implementation because it's not defined in glsl1.2
67 // https://docs.gl/sl4/smoothstep
68 float mtsmoothstep(in float edge0, in float edge1, in float x)
70 float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
71 return t * t * (3.0 - 2.0 * t);
74 #ifdef COLORED_SHADOWS
76 // c_precision of 128 fits within 7 base-10 digits
77 const float c_precision = 128.0;
78 const float c_precisionp1 = c_precision + 1.0;
80 float packColor(vec3 color)
82 return floor(color.b * c_precision + 0.5)
83 + floor(color.g * c_precision + 0.5) * c_precisionp1
84 + floor(color.r * c_precision + 0.5) * c_precisionp1 * c_precisionp1;
87 vec3 unpackColor(float value)
90 color.b = mod(value, c_precisionp1) / c_precision;
91 color.g = mod(floor(value / c_precisionp1), c_precisionp1) / c_precision;
92 color.r = floor(value / (c_precisionp1 * c_precisionp1)) / c_precision;
96 vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
98 vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
100 float visibility = step(0.0, realDistance - texDepth.r);
101 vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g));
102 if (visibility < 0.1) {
103 visibility = step(0.0, realDistance - texDepth.b);
104 result = vec4(visibility, unpackColor(texDepth.a));
111 float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
113 float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
114 float visibility = step(0.0, realDistance - texDepth);
121 #if SHADOW_FILTER == 2
122 #define PCFBOUND 2.0 // 5x5
123 #define PCFSAMPLES 25
124 #elif SHADOW_FILTER == 1
125 #define PCFBOUND 1.0 // 3x3
132 #ifdef COLORED_SHADOWS
133 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
135 vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy);
136 float depth = max(realDistance - texDepth.r, realDistance - texDepth.b);
140 float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
142 float texDepth = texture2D(shadowsampler, smTexCoord.xy).r;
143 float depth = realDistance - texDepth;
148 #define BASEFILTERRADIUS 1.0
150 float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
152 // Return fast if sharp shadows are requested
153 if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0)
158 float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance);
159 // A factor from 0 to 1 to reduce blurring of short shadows
160 float sharpness_factor = 1.0;
161 // conversion factor from shadow depth to blur radius
162 float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0;
163 if (depth > 0.0 && f_normal_length > 0.0)
164 // 5 is empirical factor that controls how fast shadow loses sharpness
165 sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0);
168 float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor
169 * f_textureresolution / 2.0 / f_shadowfar;
170 float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node
172 return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS);
175 #ifdef POISSON_FILTER
176 const vec2[64] poissonDisk = vec2[64](
177 vec2(0.170019, -0.040254),
178 vec2(-0.299417, 0.791925),
179 vec2(0.645680, 0.493210),
180 vec2(-0.651784, 0.717887),
181 vec2(0.421003, 0.027070),
182 vec2(-0.817194, -0.271096),
183 vec2(-0.705374, -0.668203),
184 vec2(0.977050, -0.108615),
185 vec2(0.063326, 0.142369),
186 vec2(0.203528, 0.214331),
187 vec2(-0.667531, 0.326090),
188 vec2(-0.098422, -0.295755),
189 vec2(-0.885922, 0.215369),
190 vec2(0.566637, 0.605213),
191 vec2(0.039766, -0.396100),
192 vec2(0.751946, 0.453352),
193 vec2(0.078707, -0.715323),
194 vec2(-0.075838, -0.529344),
195 vec2(0.724479, -0.580798),
196 vec2(0.222999, -0.215125),
197 vec2(-0.467574, -0.405438),
198 vec2(-0.248268, -0.814753),
199 vec2(0.354411, -0.887570),
200 vec2(0.175817, 0.382366),
201 vec2(0.487472, -0.063082),
202 vec2(0.355476, 0.025357),
203 vec2(-0.084078, 0.898312),
204 vec2(0.488876, -0.783441),
205 vec2(0.470016, 0.217933),
206 vec2(-0.696890, -0.549791),
207 vec2(-0.149693, 0.605762),
208 vec2(0.034211, 0.979980),
209 vec2(0.503098, -0.308878),
210 vec2(-0.016205, -0.872921),
211 vec2(0.385784, -0.393902),
212 vec2(-0.146886, -0.859249),
213 vec2(0.643361, 0.164098),
214 vec2(0.634388, -0.049471),
215 vec2(-0.688894, 0.007843),
216 vec2(0.464034, -0.188818),
217 vec2(-0.440840, 0.137486),
218 vec2(0.364483, 0.511704),
219 vec2(0.034028, 0.325968),
220 vec2(0.099094, -0.308023),
221 vec2(0.693960, -0.366253),
222 vec2(0.678884, -0.204688),
223 vec2(0.001801, 0.780328),
224 vec2(0.145177, -0.898984),
225 vec2(0.062655, -0.611866),
226 vec2(0.315226, -0.604297),
227 vec2(-0.780145, 0.486251),
228 vec2(-0.371868, 0.882138),
229 vec2(0.200476, 0.494430),
230 vec2(-0.494552, -0.711051),
231 vec2(0.612476, 0.705252),
232 vec2(-0.578845, -0.768792),
233 vec2(-0.772454, -0.090976),
234 vec2(0.504440, 0.372295),
235 vec2(0.155736, 0.065157),
236 vec2(0.391522, 0.849605),
237 vec2(-0.620106, -0.328104),
238 vec2(0.789239, -0.419965),
239 vec2(-0.545396, 0.538133),
240 vec2(-0.178564, -0.596057)
243 #ifdef COLORED_SHADOWS
245 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
247 float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
249 // we are in the middle of even brightness, no need for filtering
250 return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
254 vec4 visibility = vec4(0.0);
255 float scale_factor = radius / f_textureresolution;
257 int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
258 samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
259 int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
260 int end_offset = int(samples) + init_offset;
262 for (int x = init_offset; x < end_offset; x++) {
263 clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
264 visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
267 return visibility / samples;
272 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
274 float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
276 // we are in the middle of even brightness, no need for filtering
277 return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
281 float visibility = 0.0;
282 float scale_factor = radius / f_textureresolution;
284 int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows
285 samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples)));
286 int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples)));
287 int end_offset = int(samples) + init_offset;
289 for (int x = init_offset; x < end_offset; x++) {
290 clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy;
291 visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
294 return visibility / samples;
300 /* poisson filter disabled */
302 #ifdef COLORED_SHADOWS
304 vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
306 float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
308 // we are in the middle of even brightness, no need for filtering
309 return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance);
313 vec4 visibility = vec4(0.0);
315 float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
316 bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
317 float scale_factor = radius / bound / f_textureresolution;
321 for (y = -bound; y <= bound; y += 1.0)
322 for (x = -bound; x <= bound; x += 1.0) {
323 clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
324 visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance);
328 return visibility / max(n, 1.0);
332 float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
334 float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance);
336 // we are in the middle of even brightness, no need for filtering
337 return getHardShadow(shadowsampler, smTexCoord.xy, realDistance);
341 float visibility = 0.0;
343 float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows
344 bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound);
345 float scale_factor = radius / bound / f_textureresolution;
349 for (y = -bound; y <= bound; y += 1.0)
350 for (x = -bound; x <= bound; x += 1.0) {
351 clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy;
352 visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance);
356 return visibility / max(n, 1.0);
364 #if ENABLE_TONE_MAPPING
366 /* Hable's UC2 Tone mapping parameters
374 equation used: ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
377 vec3 uncharted2Tonemap(vec3 x)
379 return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
382 vec4 applyToneMapping(vec4 color)
384 color = vec4(pow(color.rgb, vec3(2.2)), color.a);
385 const float gamma = 1.6;
386 const float exposureBias = 5.5;
387 color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
388 // Precalculated white_scale from
389 //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
390 vec3 whiteScale = vec3(1.036015346);
391 color.rgb *= whiteScale;
392 return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
401 vec2 uv = varTexCoord.st;
403 vec4 base = texture2D(baseTexture, uv).rgba;
404 // If alpha is zero, we can just discard the pixel. This fixes transparency
405 // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
406 // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
411 #ifdef USE_DISCARD_REF
417 vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
420 #ifdef ENABLE_DYNAMIC_SHADOWS
421 if (f_shadow_strength > 0.0) {
422 float shadow_int = 0.0;
423 vec3 shadow_color = vec3(0.0, 0.0, 0.0);
424 vec3 posLightSpace = getLightSpacePosition();
426 float distance_rate = (1.0 - pow(clamp(2.0 * length(posLightSpace.xy - 0.5),0.0,1.0), 10.0));
427 if (max(abs(posLightSpace.x - 0.5), abs(posLightSpace.y - 0.5)) > 0.5)
429 float f_adj_shadow_strength = max(adj_shadow_strength-mtsmoothstep(0.9,1.1, posLightSpace.z),0.0);
431 if (distance_rate > 1e-7) {
433 #ifdef COLORED_SHADOWS
435 if (cosLight > 0.0 || f_normal_length < 1e-3)
436 visibility = getShadowColor(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
438 visibility = vec4(1.0, 0.0, 0.0, 0.0);
439 shadow_int = visibility.r;
440 shadow_color = visibility.gba;
442 if (cosLight > 0.0 || f_normal_length < 1e-3)
443 shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z);
447 shadow_int *= distance_rate;
448 shadow_int = clamp(shadow_int, 0.0, 1.0);
452 // turns out that nightRatio falls off much faster than
453 // actual brightness of artificial light in relation to natual light.
454 // Power ratio was measured on torches in MTG (brightness = 14).
455 float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);
457 // Apply self-shadowing when light falls at a narrow angle to the surface
458 // Cosine of the cut-off angle.
459 const float self_shadow_cutoff_cosine = 0.14;
460 if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) {
461 shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
462 shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine);
465 shadow_int *= f_adj_shadow_strength;
467 // calculate fragment color from components:
469 adjusted_night_ratio * col.rgb + // artificial light
470 (1.0 - adjusted_night_ratio) * ( // natural light
471 col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color
472 dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight
476 #if ENABLE_TONE_MAPPING
477 col = applyToneMapping(col);
480 // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?),
481 // the fog will only be rendered correctly if the last operation before the
482 // clamp() is an addition. Else, the clamp() seems to be ignored.
483 // E.g. the following won't work:
484 // float clarity = clamp(fogShadingParameter
485 // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0);
486 // As additions usually come for free following a multiplication, the new formula
487 // should be more efficient as well.
488 // Note: clarity = (1 - fogginess)
489 float clarity = clamp(fogShadingParameter
490 - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
491 col = mix(skyBgColor, col, clarity);
492 col = vec4(col.rgb, base.a);