diff --git a/changes/lake-water.md b/changes/lake-water.md new file mode 100644 index 00000000..5625420f --- /dev/null +++ b/changes/lake-water.md @@ -0,0 +1,8 @@ +bump: patch +type: fix +**A lake can be the water that mirrors the world** — a reflecting body is clipped to its +ellipse like any other unless it is an unbounded sea, so a map whose reflection belongs to +its lake no longer floods every hollow in the survey with that lake's level; and the +terrain's wet shore, forest and scree gates read the carved lake's line inside its outline +(`u_lake`), the rule the grass already used, so a lake above the sea keeps its wet bank and +no forest is painted down its bed. diff --git a/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv b/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv index 145a3ace..fcb7c1fe 100644 Binary files a/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv and b/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv b/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv index 9a334e93..3bb896b2 100644 Binary files a/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv and b/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv b/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv index 332dad05..7b229658 100644 Binary files a/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv and b/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv b/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv index cbe9a3c4..9b5e03a3 100644 Binary files a/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv and b/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f2952361.frag.spv b/packages/ludic.render3d/shaders/spv/f2952361.frag.spv index 3ac120b3..bb3e859d 100644 Binary files a/packages/ludic.render3d/shaders/spv/f2952361.frag.spv and b/packages/ludic.render3d/shaders/spv/f2952361.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f4382918.frag.spv b/packages/ludic.render3d/shaders/spv/f4382918.frag.spv index 085d6b8d..b46d41ff 100644 Binary files a/packages/ludic.render3d/shaders/spv/f4382918.frag.spv and b/packages/ludic.render3d/shaders/spv/f4382918.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index a76f578e..d8859f64 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -2297,7 +2297,7 @@ U 2e2908c8 vert u_node 176 vec3 1 0 U 2e2908c8 vert u_morph 192 vec2 1 0 U 2e2908c8 vert u_grid 200 float 1 0 I 2e2908c8 a_xz 0 vec2 -B 2e2908c8 frag 1 872 +B 2e2908c8 frag 1 904 U 2e2908c8 frag u_cascade_vp 0 mat4 5 64 U 2e2908c8 frag u_cascade_split 320 float 5 16 U 2e2908c8 frag u_cascade_range 400 float 5 16 @@ -2333,10 +2333,11 @@ U 2e2908c8 frag u_carpet_on 776 float 1 0 U 2e2908c8 frag u_snow_line 780 float 1 0 U 2e2908c8 frag u_lake_level 784 float 1 0 U 2e2908c8 frag u_sea_level 788 float 1 0 -U 2e2908c8 frag u_origin 792 vec2 1 0 -U 2e2908c8 frag u_view 800 mat4 1 0 -U 2e2908c8 frag u_far_split 864 float 1 0 -U 2e2908c8 frag u_far_band 868 float 1 0 +U 2e2908c8 frag u_lake 800 vec4 1 0 +U 2e2908c8 frag u_origin 816 vec2 1 0 +U 2e2908c8 frag u_view 832 mat4 1 0 +U 2e2908c8 frag u_far_split 896 float 1 0 +U 2e2908c8 frag u_far_band 900 float 1 0 T 2e2908c8 u_brdf 2 T 2e2908c8 u_carpet 3 T 2e2908c8 u_grass_a 4 @@ -2365,7 +2366,7 @@ U 1f95f31b vert u_node 176 vec3 1 0 U 1f95f31b vert u_morph 192 vec2 1 0 U 1f95f31b vert u_grid 200 float 1 0 I 1f95f31b a_xz 0 vec2 -B 1f95f31b frag 1 872 +B 1f95f31b frag 1 904 U 1f95f31b frag u_cascade_vp 0 mat4 5 64 U 1f95f31b frag u_cascade_split 320 float 5 16 U 1f95f31b frag u_cascade_range 400 float 5 16 @@ -2401,10 +2402,11 @@ U 1f95f31b frag u_carpet_on 776 float 1 0 U 1f95f31b frag u_snow_line 780 float 1 0 U 1f95f31b frag u_lake_level 784 float 1 0 U 1f95f31b frag u_sea_level 788 float 1 0 -U 1f95f31b frag u_origin 792 vec2 1 0 -U 1f95f31b frag u_view 800 mat4 1 0 -U 1f95f31b frag u_far_split 864 float 1 0 -U 1f95f31b frag u_far_band 868 float 1 0 +U 1f95f31b frag u_lake 800 vec4 1 0 +U 1f95f31b frag u_origin 816 vec2 1 0 +U 1f95f31b frag u_view 832 mat4 1 0 +U 1f95f31b frag u_far_split 896 float 1 0 +U 1f95f31b frag u_far_band 900 float 1 0 T 1f95f31b u_brdf 2 T 1f95f31b u_carpet 3 T 1f95f31b u_grass_a 4 @@ -2433,7 +2435,7 @@ U f4382918 vert u_node 176 vec3 1 0 U f4382918 vert u_morph 192 vec2 1 0 U f4382918 vert u_grid 200 float 1 0 I f4382918 a_xz 0 vec2 -B f4382918 frag 1 872 +B f4382918 frag 1 904 U f4382918 frag u_cascade_vp 0 mat4 5 64 U f4382918 frag u_cascade_split 320 float 5 16 U f4382918 frag u_cascade_range 400 float 5 16 @@ -2469,10 +2471,11 @@ U f4382918 frag u_carpet_on 776 float 1 0 U f4382918 frag u_snow_line 780 float 1 0 U f4382918 frag u_lake_level 784 float 1 0 U f4382918 frag u_sea_level 788 float 1 0 -U f4382918 frag u_origin 792 vec2 1 0 -U f4382918 frag u_view 800 mat4 1 0 -U f4382918 frag u_far_split 864 float 1 0 -U f4382918 frag u_far_band 868 float 1 0 +U f4382918 frag u_lake 800 vec4 1 0 +U f4382918 frag u_origin 816 vec2 1 0 +U f4382918 frag u_view 832 mat4 1 0 +U f4382918 frag u_far_split 896 float 1 0 +U f4382918 frag u_far_band 900 float 1 0 T f4382918 u_brdf 2 T f4382918 u_carpet 3 T f4382918 u_grass_a 4 @@ -2501,7 +2504,7 @@ U f2952361 vert u_node 176 vec3 1 0 U f2952361 vert u_morph 192 vec2 1 0 U f2952361 vert u_grid 200 float 1 0 I f2952361 a_xz 0 vec2 -B f2952361 frag 1 872 +B f2952361 frag 1 904 U f2952361 frag u_cascade_vp 0 mat4 5 64 U f2952361 frag u_cascade_split 320 float 5 16 U f2952361 frag u_cascade_range 400 float 5 16 @@ -2537,10 +2540,11 @@ U f2952361 frag u_carpet_on 776 float 1 0 U f2952361 frag u_snow_line 780 float 1 0 U f2952361 frag u_lake_level 784 float 1 0 U f2952361 frag u_sea_level 788 float 1 0 -U f2952361 frag u_origin 792 vec2 1 0 -U f2952361 frag u_view 800 mat4 1 0 -U f2952361 frag u_far_split 864 float 1 0 -U f2952361 frag u_far_band 868 float 1 0 +U f2952361 frag u_lake 800 vec4 1 0 +U f2952361 frag u_origin 816 vec2 1 0 +U f2952361 frag u_view 832 mat4 1 0 +U f2952361 frag u_far_split 896 float 1 0 +U f2952361 frag u_far_band 900 float 1 0 T f2952361 u_brdf 2 T f2952361 u_carpet 3 T f2952361 u_grass_a 4 @@ -2569,7 +2573,7 @@ U a0f2dbac vert u_node 176 vec3 1 0 U a0f2dbac vert u_morph 192 vec2 1 0 U a0f2dbac vert u_grid 200 float 1 0 I a0f2dbac a_xz 0 vec2 -B a0f2dbac frag 1 872 +B a0f2dbac frag 1 904 U a0f2dbac frag u_cascade_vp 0 mat4 5 64 U a0f2dbac frag u_cascade_split 320 float 5 16 U a0f2dbac frag u_cascade_range 400 float 5 16 @@ -2605,10 +2609,11 @@ U a0f2dbac frag u_carpet_on 776 float 1 0 U a0f2dbac frag u_snow_line 780 float 1 0 U a0f2dbac frag u_lake_level 784 float 1 0 U a0f2dbac frag u_sea_level 788 float 1 0 -U a0f2dbac frag u_origin 792 vec2 1 0 -U a0f2dbac frag u_view 800 mat4 1 0 -U a0f2dbac frag u_far_split 864 float 1 0 -U a0f2dbac frag u_far_band 868 float 1 0 +U a0f2dbac frag u_lake 800 vec4 1 0 +U a0f2dbac frag u_origin 816 vec2 1 0 +U a0f2dbac frag u_view 832 mat4 1 0 +U a0f2dbac frag u_far_split 896 float 1 0 +U a0f2dbac frag u_far_band 900 float 1 0 T a0f2dbac u_brdf 2 T a0f2dbac u_carpet 3 T a0f2dbac u_grass_a 4 @@ -2637,7 +2642,7 @@ U 5e4589a1 vert u_node 176 vec3 1 0 U 5e4589a1 vert u_morph 192 vec2 1 0 U 5e4589a1 vert u_grid 200 float 1 0 I 5e4589a1 a_xz 0 vec2 -B 5e4589a1 frag 1 872 +B 5e4589a1 frag 1 904 U 5e4589a1 frag u_cascade_vp 0 mat4 5 64 U 5e4589a1 frag u_cascade_split 320 float 5 16 U 5e4589a1 frag u_cascade_range 400 float 5 16 @@ -2673,10 +2678,11 @@ U 5e4589a1 frag u_carpet_on 776 float 1 0 U 5e4589a1 frag u_snow_line 780 float 1 0 U 5e4589a1 frag u_lake_level 784 float 1 0 U 5e4589a1 frag u_sea_level 788 float 1 0 -U 5e4589a1 frag u_origin 792 vec2 1 0 -U 5e4589a1 frag u_view 800 mat4 1 0 -U 5e4589a1 frag u_far_split 864 float 1 0 -U 5e4589a1 frag u_far_band 868 float 1 0 +U 5e4589a1 frag u_lake 800 vec4 1 0 +U 5e4589a1 frag u_origin 816 vec2 1 0 +U 5e4589a1 frag u_view 832 mat4 1 0 +U 5e4589a1 frag u_far_split 896 float 1 0 +U 5e4589a1 frag u_far_band 900 float 1 0 T 5e4589a1 u_brdf 2 T 5e4589a1 u_carpet 3 T 5e4589a1 u_grass_a 4 diff --git a/packages/ludic.render3d/shaders/terrain.frag b/packages/ludic.render3d/shaders/terrain.frag index f3cecb1d..4e7e8869 100644 --- a/packages/ludic.render3d/shaders/terrain.frag +++ b/packages/ludic.render3d/shaders/terrain.frag @@ -19,6 +19,15 @@ uniform float u_carpet_on; uniform float u_snow_line; uniform float u_lake_level; // the ground just above the water is wet and dark uniform float u_sea_level; // the sea's line: shore, forest and scree gates (terrain_sea) +uniform vec4 u_lake; // the carved lake's outline: centre x/z, half extents (zero = none) +// The water line that applies here: the sea everywhere, the lake's own inside its outline - +// the rule the grass and the game's water test use. One line for both put a lake above the +// sea with no wet shore and forest painted down its bed. +float waterLine(vec2 xz) { + float wl = u_sea_level; + if (u_lake.z > 0.0) { vec2 q = (xz - u_lake.xy) / u_lake.zw; if (dot(q, q) < 1.0) wl = max(wl, u_lake_level); } + return wl; +} uniform vec2 u_origin; // world offset of the terrain grid // The ground's own sun shadow comes from the baked height-field map (tershadow.frag), @@ -251,14 +260,14 @@ vec3 groundShade(vec3 p, vec3 N, float slope, float dist, float viewDepth, bool vec3 ocf = textureLod(u_ortho, v_huv, 2.0).rgb; float gx = ocf.g - max(ocf.r, ocf.b); // (the photograph is sampled linear: sRGB 72 is 0.06, 92 is 0.11) - forestW = smoothstep(0.12, 0.06, max(ocf.r, max(ocf.g, ocf.b))) * smoothstep(0.004, 0.012, gx) * smoothstep(u_sea_level + 0.8, u_sea_level + 1.8, p.y); + forestW = smoothstep(0.12, 0.06, max(ocf.r, max(ocf.g, ocf.b))) * smoothstep(0.004, 0.012, gx) * smoothstep(waterLine(p.xz) + 0.8, waterLine(p.xz) + 1.8, p.y); // Classify from a ~60 m blur, never from the pixels: the survey's 10 m pixels carry // a foot trail as a broken line of bare ground, and thresholding them painted it // across the meadow as tan dashes (and, on the CPU, lined boulders up along it). vec3 ocl = textureLod(u_ortho, v_huv, 2.5).rgb; float mxc = max(ocl.r, max(ocl.g, ocl.b)), mnc = min(ocl.r, min(ocl.g, ocl.b)); float greenEx = ocl.g - max(ocl.r, ocl.b); - screeC = smoothstep(0.008, -0.002, greenEx) * smoothstep(0.06, 0.12, mxc) * (1.0 - smoothstep(0.55, 0.75, mxc)) * smoothstep(u_sea_level + 0.2, u_sea_level + 1.2, p.y); + screeC = smoothstep(0.008, -0.002, greenEx) * smoothstep(0.06, 0.12, mxc) * (1.0 - smoothstep(0.55, 0.75, mxc)) * smoothstep(waterLine(p.xz) + 0.2, waterLine(p.xz) + 1.2, p.y); snowC = smoothstep(0.08, 0.04, mxc - mnc) * smoothstep(0.55, 0.8, mxc); } // snow lingering in the high gullies is drawn further down, but whether it can be @@ -377,7 +386,7 @@ vec3 groundShade(vec3 p, vec3 N, float slope, float dist, float viewDepth, bool // the bank here: identical noise fields, identical time, identical phase, keyed off // height above the lake instead of depth below it. Across the seam the two agree, so // there is nothing there to read as an edge. - float above = p.y - u_sea_level; // >0 on land, metres + float above = p.y - waterLine(p.xz); // >0 on land, metres // wet ground: darker and glossier near the water, fading out over ~1.2 m float wet = smoothstep(1.2, 0.0, above); alb *= mix(1.0, 0.5, wet * 0.85); diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 9d7da8c7..c4f56e70 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -480,6 +480,7 @@ function terrain_bind_prog(p: int) -> void { var sea = lake if ter_sea_set { sea = ter_sea_level } u_f(gpu_uniform(p, "u_sea_level"), sea) + u_f4(gpu_uniform(p, "u_lake"), ter_lake_cx, ter_lake_cz, ter_lake_ex, ter_lake_ez) u_mat4(gpu_uniform(p, "u_view"), cam_view) u_mat4(gpu_uniform(p, "u_proj"), cam_proj) u_f2(gpu_uniform(p, "u_origin"), ter_ox, ter_oz) diff --git a/packages/ludic.render3d/water.ludic b/packages/ludic.render3d/water.ludic index 922908e4..856c4691 100644 --- a/packages/ludic.render3d/water.ludic +++ b/packages/ludic.render3d/water.ludic @@ -361,9 +361,12 @@ function water_draw(depth_tex: int) -> void { var r = F_ZERO if i == wb_primary { r = ron } u_f(gpu_uniform(p, "u_refl_on"), r) - # every body but the mirrored sea is clipped to the ellipse inside its bounds + # Every body is clipped to the ellipse inside its bounds but an unbounded mirrored sea, + # whose rectangle is the point. A mirrored LAKE is clipped like any other: a map whose + # reflection belongs to its lake (the Bells in Maroon Lake) would otherwise draw that + # lake's level over every hollow in the survey. var clip = F_ONE - if i == wb_primary { clip = F_ZERO } + if i == wb_primary and f_gt(f_max(wb_ex[i], wb_ez[i]), fi(10000)) { clip = F_ZERO } u_f(gpu_uniform(p, "u_clip_ellipse"), clip) mesh_draw(water_mesh) }