1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Visual Effects Vol. 1 (#14610)

This PR adds a variety of effects to enhance the visual experience.

    "soft" clouds look
    Tinted shadows
    Crude water reflections (sky and sun) and waves
    Translucent foliage
    Node specular highlights
    Adjusted fog color (more saturated where the fog is lighter)
    Minor changes to volumetric lighting (crudely simulates the effect of depth)

Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
GefullteTaubenbrust2 2024-09-24 20:14:27 +02:00 committed by GitHub
parent 4ac86db8e3
commit d8f1daac25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 356 additions and 53 deletions

View file

@ -1,3 +1,7 @@
#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT)
#define MATERIAL_WAVING_LIQUID 1
#endif
uniform sampler2D baseTexture;
uniform vec3 dayLight;
@ -7,6 +11,7 @@ uniform float fogShadingParameter;
// The cameraOffset is the current center of the visible world.
uniform highp vec3 cameraOffset;
uniform vec3 cameraPosition;
uniform float animationTimer;
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
@ -20,6 +25,7 @@ uniform float animationTimer;
uniform vec4 CameraPos;
uniform float xyPerspectiveBias0;
uniform float xyPerspectiveBias1;
uniform vec3 shadow_tint;
varying float adj_shadow_strength;
varying float cosLight;
@ -47,6 +53,49 @@ varying highp vec3 eyeVec;
varying float nightRatio;
#ifdef ENABLE_DYNAMIC_SHADOWS
#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER)
vec4 perm(vec4 x)
{
return mod(((x * 34.0) + 1.0) * x, 289.0);
}
// Corresponding gradient of snoise
vec3 gnoise(vec3 p){
vec3 a = floor(p);
vec3 d = p - a;
vec3 dd = 6.0 * d * (1.0 - d);
d = d * d * (3.0 - 2.0 * d);
vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);
vec4 k1 = perm(b.xyxy);
vec4 k2 = perm(k1.xyxy + b.zzww);
vec4 c = k2 + a.zzzz;
vec4 k3 = perm(c);
vec4 k4 = perm(c + 1.0);
vec4 o1 = fract(k3 * (1.0 / 41.0));
vec4 o2 = fract(k4 * (1.0 / 41.0));
vec4 o3 = o2 * d.z + o1 * (1.0 - d.z);
vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x);
vec4 dz1 = (o2 - o1) * dd.z;
vec2 dz2 = dz1.yw * d.x + dz1.xz * (1.0 - d.x);
vec2 dx = (o3.yw - o3.xz) * dd.x;
return vec3(
dx.y * d.y + dx.x * (1. - d.y),
(o4.y - o4.x) * dd.y,
dz2.y * d.y + dz2.x * (1. - d.y)
);
}
vec2 wave_noise(vec3 p, float off) {
return (gnoise(p + vec3(0.0, 0.0, off)) * 0.4 + gnoise(2.0 * p + vec3(0.0, off, off)) * 0.2 + gnoise(3.0 * p + vec3(0.0, off, off)) * 0.225 + gnoise(4.0 * p + vec3(-off, off, 0.0)) * 0.2).xz;
}
#endif
// assuming near is always 1.0
float getLinearDepth()
@ -66,6 +115,14 @@ float mtsmoothstep(in float edge0, in float edge1, in float x)
return t * t * (3.0 - 2.0 * t);
}
float shadowCutoff(float x) {
#if defined(ENABLE_TRANSLUCENT_FOLIAGE) && MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES
return mtsmoothstep(0.0, 0.002, x);
#else
return step(0.0, x);
#endif
}
#ifdef COLORED_SHADOWS
// c_precision of 128 fits within 7 base-10 digits
@ -92,10 +149,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist
{
vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba;
float visibility = step(0.0, realDistance - texDepth.r);
float visibility = shadowCutoff(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 - texDepth.b);
visibility = shadowCutoff(realDistance - texDepth.b);
result = vec4(visibility, unpackColor(texDepth.a));
}
return result;
@ -106,7 +163,7 @@ 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 - texDepth);
float visibility = shadowCutoff(realDistance - texDepth);
return visibility;
}
@ -378,6 +435,9 @@ void main(void)
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
// Fragment normal, can differ from vNormal which is derived from vertex normals.
vec3 fNormal = vNormal;
if (f_shadow_strength > 0.0) {
float shadow_int = 0.0;
vec3 shadow_color = vec3(0.0, 0.0, 0.0);
@ -414,12 +474,19 @@ void main(void)
// Power ratio was measured on torches in MTG (brightness = 14).
float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6);
float shadow_uncorrected = shadow_int;
// 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.035;
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);
#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES || MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS)
// Prevents foliage from becoming insanely bright outside the shadow map.
shadow_uncorrected = mix(shadow_int, shadow_uncorrected, clamp(distance_rate * 4.0 - 3.0, 0.0, 1.0));
#endif
}
shadow_int *= f_adj_shadow_strength;
@ -428,8 +495,60 @@ void main(void)
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
col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color
dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight
vec3 reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0);
vec3 viewVec = normalize(worldPosition + cameraOffset - cameraPosition);
// Water reflections
#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER)
vec3 wavePos = worldPosition * vec3(2.0, 0.0, 2.0);
float off = animationTimer * WATER_WAVE_SPEED * 10.0;
wavePos.x /= WATER_WAVE_LENGTH * 3.0;
wavePos.z /= WATER_WAVE_LENGTH * 2.0;
// This is an analogous method to the bumpmap, except we get the gradient information directly from gnoise.
vec2 gradient = wave_noise(wavePos, off);
fNormal = normalize(normalize(fNormal) + vec3(gradient.x, 0., gradient.y) * WATER_WAVE_HEIGHT * abs(fNormal.y) * 0.25);
reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0);
float fresnel_factor = dot(fNormal, viewVec);
float brightness_factor = 1.0 - adjusted_night_ratio;
// A little trig hack. We go from the dot product of viewVec and normal to the dot product of viewVec and tangent to apply a fresnel effect.
fresnel_factor = clamp(pow(1.0 - fresnel_factor * fresnel_factor, 8.0), 0.0, 1.0) * 0.8 + 0.2;
col.rgb *= 0.5;
vec3 reflection_color = mix(vec3(max(fogColor.r, max(fogColor.g, fogColor.b))), fogColor.rgb, f_shadow_strength);
// Sky reflection
col.rgb += reflection_color * pow(fresnel_factor, 2.0) * 0.5 * brightness_factor;
vec3 water_reflect_color = 12.0 * dayLight * fresnel_factor * mtsmoothstep(0.85, 0.9, pow(clamp(dot(reflect_ray, viewVec), 0.0, 1.0), 32.0)) * max(1.0 - shadow_uncorrected, 0.0);
// This line exists to prevent ridiculously bright reflection colors.
water_reflect_color /= clamp(max(water_reflect_color.r, max(water_reflect_color.g, water_reflect_color.b)) * 0.375, 1.0, 400.0);
col.rgb += water_reflect_color * f_adj_shadow_strength * brightness_factor;
#endif
#if (defined(ENABLE_NODE_SPECULAR) && !defined(MATERIAL_WAVING_LIQUID))
// Apply specular to blocks.
if (dot(v_LightDirection, vNormal) < 0.0) {
float intensity = 2.0 * (1.0 - (base.r * varColor.r));
const float specular_exponent = 5.0;
const float fresnel_exponent = 4.0;
col.rgb +=
intensity * dayLight * (1.0 - nightRatio) * (1.0 - shadow_uncorrected) * f_adj_shadow_strength *
pow(max(dot(reflect_ray, viewVec), 0.0), fresnel_exponent) * pow(1.0 - abs(dot(viewVec, fNormal)), specular_exponent);
}
#endif
#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES) && defined(ENABLE_TRANSLUCENT_FOLIAGE)
// Simulate translucent foliage.
col.rgb += 4.0 * dayLight * base.rgb * normalize(base.rgb * varColor.rgb * varColor.rgb) * f_adj_shadow_strength * pow(max(-dot(v_LightDirection, viewVec), 0.0), 4.0) * max(1.0 - shadow_uncorrected, 0.0);
#endif
}
#endif
@ -444,7 +563,13 @@ void main(void)
// Note: clarity = (1 - fogginess)
float clarity = clamp(fogShadingParameter
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
col = mix(fogColor, col, clarity);
float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b);
// Prevent zero division.
if (fogColorMax < 0.0000001) fogColorMax = 1.0;
// For high clarity (light fog) we tint the fog color.
// For this to not make the fog color artificially dark we need to normalize using the
// fog color's brightest value. We then blend our base color with this to make the fog.
col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity);
col = vec4(col.rgb, base.a);
gl_FragData[0] = col;

View file

@ -256,7 +256,9 @@ void main(void)
z_bias *= pFactor * pFactor / f_textureresolution / f_shadowfar;
shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (shadow_pos + vec4(normalOffsetScale * nNormal, 0.0))).xyz;
#if !defined(ENABLE_TRANSLUCENT_FOLIAGE) || MATERIAL_TYPE != TILE_MATERIAL_WAVING_LEAVES
shadow_position.z -= z_bias;
#endif
perspective_factor = pFactor;
if (f_timeofday < 0.2) {

View file

@ -20,6 +20,7 @@ uniform float animationTimer;
uniform vec4 CameraPos;
uniform float xyPerspectiveBias0;
uniform float xyPerspectiveBias1;
uniform vec3 shadow_tint;
varying float adj_shadow_strength;
varying float cosLight;
@ -432,7 +433,7 @@ void main(void)
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
col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color
dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight
}
#endif
@ -448,7 +449,13 @@ void main(void)
// Note: clarity = (1 - fogginess)
float clarity = clamp(fogShadingParameter
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
col = mix(fogColor, col, clarity);
float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b);
// Prevent zero division.
if (fogColorMax < 0.0000001) fogColorMax = 1.0;
// For high clarity (light fog) we tint the fog color.
// For this to not make the fog color artificially dark we need to normalize using the
// fog color's brightest value. We then blend our base color with this to make the fog.
col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity);
col = vec4(col.rgb, base.a);
gl_FragData[0] = col;

View file

@ -46,7 +46,9 @@ float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth)
if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.)
result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0;
}
return result / samples;
// We use the depth map to approximate the effect of depth on the light intensity.
// The exponent was chosen based on aesthetic preference.
return result / samples * pow(texture2D(depthmap, uv).r, 128.0);
}
vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection)