Repository navigation
Etna 0.8.1: solid water reflections, thin shoreline foam, shadows that soften with distance - #3
Draft
Atomsk-debug wants to merge 14 commits into
Draft
Atomsk-debug wants to merge 14 commits into
Atomsk-debug wants to merge 14 commits into
Conversation
Reflections: fixed steps (24 + 6 halvings, no per-pixel noise, one fixed pixel per 2x2 block), a hit only where the ray runs into a surface rather than going behind something in front of it, misses written with their distance so the upsampler averages them (0.8.0 skipped them, so one lone hit grew into a speck), a 16-texel steady upsample, and the ray following a quarter of the swell's slope. The sky's reflection that is taken out keeps the full swell, as the water shader drew it. Foam: no foam over shallow floors and none on water flowing down a slope (new etnaWaterLevel flag from the unsnapped face normal); thinner, fainter line (0.2 blocks, 0.7) broken by fine bubbles, pale non-glowing colour. Version 0.8.1, changelog. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
Contact hardening (the PCSS idea): 8 gathers look for casters round the point; the blur radius is the softness plus 0.025 x (softness - 0.25) of their average distance along the light, capped at 1.2 blocks / 32 texels. The near part keeps the exact area-weighted box, the wide part a 12-point bilinear-PCF Vogel disk, blended between. No casters found: lit at once. At the lowest softness all of it is skipped. Low sun: shadows fade up to 35% lighter below ~20 degrees. From a review of the shadow code: receivers beyond the shadow casters' chunk box (16 * ceil((distance + 14) / 16) blocks along world x/z) fade to lit instead of ending at a hard line; points beyond the map's far depth plane count as lit (terrain filter, unfiltered test, leaves, shafts) instead of shadowed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
The shadows that soften with distance read the map up to 1.2 blocks round a point and compare it with the surface's plane over that disc, but the shadow pass still dropped casters by the old filter's reach (softness + 1 texels). Near the camera that is a few hundredths of a block, so a caster just outside the view, or a little below everything on screen, could be left out of the map while a pixel's blur still read where it would be: part of a soft edge missing at the side of the screen, coming back as the camera turns. The view test's margins (sideways and behind) and the receiver floor now take the wider reach into account; nothing changes at the lowest softness, where the wider blur is off. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
Contact hardening no longer turns its reads every frame or gives up where a few reads find nothing: the box filter now also reports blockers, six fixed bilinear search reads cover the ring beyond it, the disc (16 reads, at most 16 texels) is skipped where everything read is blocked or open, and only discs wider than about eight texels turn, by more the wider they are. In the offline bench the flicker after TAA is 35-65 % below the tested build's and below 0.8.0's with a low sun. The shadow map warp and the water's fresnelDielectric are rewritten from first principles and the Cook-Torrance form, without Photon's code: the warp's distance n = sqrt(1 - (1-x^2)(1-y^2)) fills the whole map and keeps far texels two to five times finer towards the corners. EtnaShadows' margins follow its bounds. CREDITS and README list them as Etna's own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
The water's top took the sharp shadow of its own point as the body's light, while the bottom seen through it had its own shadow, shifted by the depth; the two copies crossed each other out. The body's sun share is now the shadow at four points down the refracted view, placed at equal shares of the light scattered back above the bottom (green extinction down along the sun and back up along the view), each a bilinear 2x2 lookup (new shadowSoftAt). Clear shallow water shows the bottom's shadow; murky or deep water a soft shadow in the water. Water whose bottom the shadow map can't show (wide shade, a roof) went back to the plain, far more opaque look in the shadow's shape. A fourth ring at 8 blocks looks for the bottom further round, and where none is found the depth model keeps going with 4 blocks of water. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
A ray that only just meets or misses an edge went either way from frame to frame (the jittered picture's edges move by up to half a pixel, the waves move on), and a whole half-resolution texel, spread over its neighbours by the world pass, changed at once: too smooth for the TAA's neighbourhood clip to tell from a real change, so the reflection flickered. The reflections pass now traces from the pixel's unjittered centre and reads depth and colour at the jittered spot that holds each point, and with anti-aliasing on it blends each texel with its own history (SSR_BLEND 0.3, more of this frame while the camera moves), reprojected by the water point's position with PrevViewProj and kept only where last frame held water at that distance. The history is two persistent half- size targets, water_ssr0/1, swapped with the TAA history (PostChainMixin); the plain chain binds the shafts as a placeholder and never reads it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
The benchmark holds the sun, so no sun-step fade happens during a run and its table never shows what the fades cost in play (every sunlit pixel filtering both maps for part of the time). benchmarkFade, set and put back by EtnaBenchmark, keeps a fade running along the held direction, held at its middle once the second map is drawn: both maps drawn in turn and read half and half, as in a real fade. Off by default; nothing else changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
Far water seen at a grazing angle changes its distance from the camera by several blocks a pixel, more than the history check's 0.3 + 3 % allowed, so the reflections pass threw its history away exactly where the shore's reflection flickers most (from about 60 blocks out, sooner from a boat). The check now also allows four pixels' change in distance on the water (the history texels lie up to two pixels away, plus last frame's jitter), capped at a quarter of the distance. The water body's four shadow points go down along the bent view, so where the bottom rises ahead of it (a beach stepping up out of a lake) the deeper ones ended up inside the sand and read the bottom above them as a shadow: a darker band in front of the steps. Each point is now looked up where its way to the sun crosses the water's surface (the same texels of the map), so only what stands above the water shades it. The header no longer claims a still scene traces the same rays every frame: each ray starts from the point the jittered pixel shows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
The warp is centred on the map's middle, so each time the middle moved, every texel of the map landed somewhere else on the world by a fraction of a texel and the map took every edge and every leaf's holes at new points. It moved a texel (0.1 blocks) at a time: while walking, most frames. The dappled shade under trees and the shadow edges further out shimmered for as long as the camera moved. Now the middle holds still in the world and steps after the camera once it is three quarters of a 2-block step away on an axis of the map (a hold, so it can't flick back and forth). Offline (a canopy of leaf blocks with holes, swaying, under the area-weighted filter, walking at 4.3 blocks a second) the mean change from one frame to the next drops from 0.05-0.09 to 0.004-0.008 of full shade, 16 to 150 blocks away; the frame the map steps is no worse than any frame was before. Near the camera texels get at most 7% wider (16% along the line to the middle). The shadows fade out towards the map's edge counted from the camera (etnaMapOut, EtnaShadowInfo.w: the share of the radius the map always covers round the camera), so where they end no longer jumps when the middle steps. The caster tests count from the middle (the square, the caster floor) or widen by the camera's lead (the columns, the view margins). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
etnaMapOut now returns at least 1 for a point outside the map itself, so every reader (filter, leaf light, single and bilinear tests, water) treats it as past the edge, as post/shafts.fsh already did. It only happens while the maps fade, when the older map is read a frame late behind a camera flying faster than the slack a frame; those points were nearly faded out but read the map's clamped edge texels. The middle-step comment now gives the texel growth for both the 160 and the 96-block distance, and says how much shorter the shadows end. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
…mera From 20 to 44 blocks out the shadow pass fades the sway out. Further away a texel of the map is wider than the sway, which then only flips leaf texels between lit and shaded every frame: far tree shade twinkled even with the camera still. On screen the sway is under a pixel there anyway, and the leaves themselves keep swaying, by less than their shadow lookup's offset and bias (1.5 texels along the light plus the thin bias). Offline (leaflab, swaying leaf blocks with holes, area-weighted filter, the 2-block map steps): with the camera still the change per frame at 32-150 blocks goes from 0.003-0.004 to none; walking it drops by about 40%, the rest coming from the map's steps. Average shade unchanged. The far vertices also skip the wind maths. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
waterBodyShadow follows the straight line of sight into the water instead of the refracted ray. The bottom shows through the water unbent (the blend lays the water over it), so the shade in the water now lands on the shadow seen on the bottom. Before, the two sat side by side: shaded water over a lit bottom looked colourless and see-through, lit water over a shaded bottom like a patch of colour. Far water seen nearly edge on: the waves a pixel no longer resolves still tilt the facets it sees towards the eye (HIDDEN_TILT, 0.08), so they mirror sky from a little above the horizon and less of it than a flat mirror (waterSeenCos, waterSeenMirror in water_waves.glsl, used by the terrain and by the reflections pass alike). From a block or two above the water, far water mirrored almost only the whitish horizon. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before: in 0.8.0, the shore, trees and hills mirrored in lakes and rivers break up into hundreds of scattered dark dots instead of keeping their shapes. Shoreline foam is a thick, glowing white band. Shallow water turns milky, and thin sheets of water flowing down a bank are covered in blotches. Shadows keep the same sharp edge however far they reach from what casts them, so long low-sun shadows look like hard black strips. Past the shadow casters' range, they stop at a hard line. In the first 0.8.1 test build, reflections and far shadows shimmered, and a shadow falling on water showed twice, the two copies cancelling each other. While walking, the dappled shade under trees and the shadow edges further out changed on almost every frame, and far tree shade twinkled even standing still.
After: reflections are solid shapes that wobble gently with the waves and stay steady from frame to frame. Foam is a thin, faint, bubbly line where still water meets the shore. Shallow water stays clear, and water flowing down a slope has no foam. Shadows are sharp where they touch their caster and blur as they stretch away from it, as in real life, without shimmering. At sunrise and sunset they are lighter. At the edge of the casters' range they fade out instead of ending. A shadow on water is one shadow: on clear shallow water it is the bottom's, on deep or murky water a soft shade inside the water. While walking, the shade under trees holds still and only changes slightly about twice a second, and the far end of the shadows stays at the same distance. Standing still, far tree shade is completely still. The shadow map warp and the water's Fresnel are Etna's own code now, not Photon's.
How:
post/water_ssr.fsh:PrevViewProj. A history texel is only used where last frame held water at the same distance, allowing for how fast that distance changes across a few pixels on grazing far water.tools/gen_post_effects.py,etna_world.json,etna_world_plain.json,PostChainMixin: two persistent half-size targets,water_ssr0/1, hold that history and are swapped together with the TAA history. The plain chain binds a placeholder it never reads.include/water_reflection.glsl: the upsampler uses the same 16 texels every frame, and misses count. In 0.8.0 it skipped them, so one lone hit grew into a speck.include/terrain_water.glsl:fresnelDielectricis rewritten in the exact Cook-Torrance form.waterBodyShadow: the water's sun share is the shadow at four points down the refracted view, placed at equal shares of the light scattered back above the bottom (green extinction down along the sun and back up along the view). Each point is looked up where its way to the sun crosses the surface, so only what stands above the water shades it, and a bottom rising ahead of the view doesn't. This replaces the surface's own sharp shadow, which crossed out the bottom's shifted one.core/terrain.fsh: newetnaWaterLevelflag, taken from the face normal before it is snapped to an axis. Water faces takewaterBodyShadow.include/shadow_distortion.glsl: Etna's own warp, factor = 0.85 n + 0.15 with n = sqrt(1 - (1 - x^2)(1 - y^2)). It fills the whole map and keeps far texels two to five times finer towards the corners.core/terrain.vsh: in the shadow pass, leaves and plants sway only near the camera (fading out from 20 to 44 blocks). Further out a map texel is wider than the sway, which only flipped leaf texels between lit and shaded every frame. The leaves still sway in the main pass, by less than their shadow lookup's offset and bias.include/terrain_shadow.glsl:shadowSoftAt: a bilinear 2x2 test of any point, used by the water.post/shafts.fsh.etnaMapOut: how far out a point lies, counted from the camera (shadowMat[3].xy) overEtnaShadowInfo.w, the share of the radius the map always covers round the camera. Every reader (filter, single and bilinear tests, leaf light, water,post/shafts.fsh) fades and stops by it, so where the shadows end doesn't move when the map's middle steps. They end about 3.5 blocks short of the shadow distance (the hold plus 2 blocks of slack). A point off the map itself also counts as past the edge (a map read a frame late behind a fast-flying camera).EtnaShadows.java:benchmarkFade, off by default: the benchmark can hold a sun-step fade at its middle, to measure the cost of reading both maps while the sun moves.followMiddlekeeps each map's middle fixed in the world and steps it by 2 blocks (whole texels) once the camera is 1.5 blocks from it on an axis. Near the camera texels are at most 7% wider. The caster tests count from the middle (the square, the caster floor) or widen by the camera's lead (the columns, the view margins).EtnaShadowInfo.w(unread since the shafts became a post pass) now carries the reach.The dot analysis was cross-checked against the look thread's own patch: the calmer ray normal and the steady 16-texel upsample come from it.
Testing so far:
tools/validate_shaders.py.The latest build has not been tested in game yet.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Xzo7zbTpyU7a3y3XQNbw1n
Generated by Claude Code