From 20cf81b3622e89e3b20c5ee638504e0fa87bec66 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 14 Oct 2026 15:02:14 +0200 Subject: [PATCH] fix(shaders): parallax mapping reads its depth from a mip about four texels per pixel coarser, filtered bicubically where the hit is refined, so the relief no longer breaks into stalactites and the edges of stones no longer step along the texel grid of the compressed depth; it takes 16 to 64 layers by the angle of view and six steps of refinement, casts the shadows of the relief from the sun over the sun light and darkens the hollows deeper than the mean depth of the texture while lightening the tops, so the mean brightness of a surface stays the same --- shaders/light_common.glsl | 11 +++++++ shaders/parallax.glsl | 66 +++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/shaders/light_common.glsl b/shaders/light_common.glsl index 0801b09d..6549fbdf 100644 --- a/shaders/light_common.glsl +++ b/shaders/light_common.glsl @@ -36,6 +36,11 @@ float length2(vec3 v) return dot(v, v); } +#ifdef PARALLAX +float parallax_occlusion = 0.0; +float parallax_shadow = 0.0; +#endif + float calc_shadow() { #if SHADOWMAP_ENABLED @@ -289,6 +294,9 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl // Sun, headlights and emission are added afterwards so they are NOT // attenuated by AMBIENT_SCALE - this only dims the indirect term. fragcolor *= AMBIENT_SCALE; +#ifdef PARALLAX + fragcolor *= 1.0 - parallax_occlusion; +#endif vec3 emissioncolor = vec3(emission); @@ -331,6 +339,9 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl float shadow1 = 0.0; if (shadowtone < 1.0) shadow1 = (1.0 - shadowtone) * clamp(calc_shadow(), 0.0, 1.0); +#ifdef PARALLAX + shadow1 = max(shadow1, parallax_shadow); +#endif // Sun HDR scale -> SUN_DIFFUSE_SCALE (default 2.5). Controls how // bright sun-lit (unshaded) faces get. Lower this if surfaces in diff --git a/shaders/parallax.glsl b/shaders/parallax.glsl index b5107e81..fbfa79dc 100644 --- a/shaders/parallax.glsl +++ b/shaders/parallax.glsl @@ -1,28 +1,71 @@ +float ParallaxDepth(vec2 f_coord, float lod) +{ + vec2 size = vec2(textureSize(normalmap, int(lod))); + vec2 st = f_coord * size - 0.5; + vec2 cell = floor(st); + vec2 f = st - cell; + vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; + vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; + vec2 w3 = f * f * f / 6.0; + vec2 w2 = 1.0 - w0 - w1 - w3; + vec2 g0 = w0 + w1; + vec2 g1 = w2 + w3; + vec2 h0 = (cell - 0.5 + w1 / g0) / size; + vec2 h1 = (cell + 1.5 + w3 / g1) / size; + return g0.y * (g0.x * textureLod(normalmap, h0, lod).b + g1.x * textureLod(normalmap, vec2(h1.x, h0.y), lod).b) + + g1.y * (g0.x * textureLod(normalmap, vec2(h0.x, h1.y), lod).b + g1.x * textureLod(normalmap, h1, lod).b); +} + +float ParallaxShadow(vec2 f_coord, float depth, float lod, float scale) +{ + if (lights_count == 0U || depth <= 0.0) { + return 0.0; + } + vec3 lightDir = normalize(transpose(f_tbn) * normalize(-lights[0].dir)); + if (lightDir.z <= 0.0) { + return 0.0; + } + const int steps = 12; + float layerDepth = depth / float(steps); + vec2 deltaTexCoords = lightDir.xy / max(lightDir.z, 0.25) * scale * layerDepth; + float shadow = 0.0; + for (int i = 1; i <= steps; ++i) + { + float rayDepth = depth - layerDepth * float(i); + float surface = ParallaxDepth(f_coord + deltaTexCoords * float(i), lod); + shadow = max(shadow, (rayDepth - surface) * 12.0 * (1.0 - float(i) / float(steps + 1))); + } + return clamp(shadow, 0.0, 1.0); +} + vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir, float scale, float offset) { - vec2 dx = dFdx(f_coord); - vec2 dy = dFdy(f_coord); + vec2 size = vec2(textureSize(normalmap, 0)); + float footprint = max(length(dFdx(f_coord) * size), length(dFdy(f_coord) * size)); + float lod = clamp(log2(max(footprint, 1e-4)) + 2.0, 0.0, log2(max(size.x, size.y))); float fade = (1.0 - smoothstep(60.0, 100.0, length(f_pos.xyz))) * smoothstep(0.0, 0.1, viewDir.z); if (fade <= 0.0) { return f_coord; } + float meanDepth = textureLod(normalmap, f_coord, log2(max(size.x, size.y))).b; #if EXTRAEFFECTS_ENABLED - const float minLayers = 8.0; - const float maxLayers = 32.0; + const float minLayers = 16.0; + const float maxLayers = 64.0; float numLayers = mix(maxLayers, minLayers, clamp(viewDir.z, 0.0, 1.0)); float layerDepth = 1.0 / numLayers; vec2 deltaTexCoords = viewDir.xy * scale * fade / numLayers; vec2 currentTexCoords = f_coord; float currentLayerDepth = 0.0; - float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dx, dy).b; - for (int i = 0; i < 40 && currentLayerDepth < currentDepthMapValue; ++i) + float currentDepthMapValue = textureLod(normalmap, currentTexCoords, lod).b; + for (int i = 0; i < 100 && currentLayerDepth < currentDepthMapValue; ++i) { currentTexCoords -= deltaTexCoords; currentLayerDepth += layerDepth; - currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dx, dy).b; + currentDepthMapValue = textureLod(normalmap, currentTexCoords, lod).b; } - for (int i = 0; i < 5; ++i) + currentDepthMapValue = ParallaxDepth(currentTexCoords, lod); + for (int i = 0; i < 6; ++i) { deltaTexCoords *= 0.5; layerDepth *= 0.5; @@ -34,11 +77,14 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir, float scale, float offset) currentTexCoords += deltaTexCoords; currentLayerDepth -= layerDepth; } - currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dx, dy).b; + currentDepthMapValue = ParallaxDepth(currentTexCoords, lod); } + parallax_occlusion = clamp(0.8 * (currentLayerDepth - meanDepth), -0.3, 0.6) * fade; + parallax_shadow = ParallaxShadow(currentTexCoords, currentLayerDepth, lod, scale * fade) * fade; return currentTexCoords; #else - float height = textureGrad(normalmap, f_coord, dx, dy).b; + float height = ParallaxDepth(f_coord, lod); + parallax_occlusion = clamp(0.8 * (height - meanDepth), -0.3, 0.6) * fade; vec2 p = viewDir.xy / max(viewDir.z, 0.2) * (height * (scale - offset) * 0.2 * fade); return f_coord - p; #endif