feat(render3d): terrain_lake_carve - a height map may carry its own lake bed

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-16 15:27:21 +03:00
parent 6dfdee75e2
commit fa6c422f9b
5 changed files with 16 additions and 2 deletions

View file

@ -6,3 +6,6 @@ its lake no longer floods every hollow in the survey with that lake's level; and
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.
`terrain_lake_carve(false)` keeps a lake's line, outline and shore but skips carving its
bed, for a height map that already carries a shaped one.

View file

@ -10,6 +10,7 @@ uniform float u_dem_base; // the elevation that becomes y = 0
uniform vec2 u_origin; // world x/z of the map's centre
uniform vec4 u_lake; // a lake: centre x/z, half extents (zero = none)
uniform float u_lake_level; // its surface height; the model records the surface, the bed is carved below it
uniform float u_lake_carve; // 0: the height map carries its own bed (terrain_lake_carve)
uniform float u_sea_level; // the sea the coast falls into (terrain_sea); the lake's level unless set
uniform vec4 u_isle; // an island: centre x/z, radius (or rim margin), falloff width
uniform float u_isle_mode; // 0 none, 1 radial (terrain_island), 2 inward from the survey's edge (terrain_coast)
@ -50,7 +51,7 @@ void main() {
float raw = demRaw(v_uv) * (u_dem_max - u_dem_min) + u_dem_min - u_dem_base;
// the survey carries its own relief at this resolution; only the foot track is added
h -= 0.25 * smoothstep(6.0, 1.5, pathDist(xz));
if (u_lake.z > 0.0) {
if (u_lake.z > 0.0 && u_lake_carve > 0.5) {
// the elevation model samples the water surface as flat ground: inside the lake's
// outline sink it into a bed, deepest in the middle, with a gentle gravel ramp at the shore
vec2 q = (xz - u_lake.xy) / u_lake.zw;

View file

@ -157,7 +157,8 @@ U 4be7acd9 frag u_dem_base 776 float 1 0
U 4be7acd9 frag u_origin 784 vec2 1 0
U 4be7acd9 frag u_lake 800 vec4 1 0
U 4be7acd9 frag u_lake_level 816 float 1 0
U 4be7acd9 frag u_sea_level 820 float 1 0
U 4be7acd9 frag u_lake_carve 820 float 1 0
U 4be7acd9 frag u_sea_level 824 float 1 0
U 4be7acd9 frag u_isle 832 vec4 1 0
U 4be7acd9 frag u_isle_mode 848 float 1 0
U 4be7acd9 frag u_dem_blur 852 float 1 0

View file

@ -69,6 +69,12 @@ var ter_lake_ez: int = 0
function terrain_lake(level: int, cx: int, cz: int, ex: int, ez: int) -> void {
ter_lake_level = level; ter_lake_cx = cx; ter_lake_cz = cz; ter_lake_ex = ex; ter_lake_ez = ez
}
# Whether the generator carves that bed. A height map that already carries a shaped bed
# turns it off and keeps the lake for everything else (its line, its outline, its shore):
# the carve drops the bed metres within the survey's last 0.9 m of shore, which reads as a
# cut edge on a lake whose own bank was shaped to come down gently.
var ter_lake_carve: bool = true
function terrain_lake_carve(on: bool) -> void { ter_lake_carve = on }
# The SEA's level, for the coast, the strand and the shoreline shading, when it is not the
# carved lake's. They were one number, which holds only while the lake is at sea level: a
# lake eighty metres up the valley drowned the whole coast toward its own surface. Unset,
@ -238,6 +244,9 @@ function terrain_generate() -> void {
u_f2(gpu_uniform(p, "u_origin"), ter_ox, ter_oz)
u_f4(gpu_uniform(p, "u_lake"), ter_lake_cx, ter_lake_cz, ter_lake_ex, ter_lake_ez)
u_f(gpu_uniform(p, "u_lake_level"), ter_lake_level)
var carve = F_ONE
if not ter_lake_carve { carve = F_ZERO }
u_f(gpu_uniform(p, "u_lake_carve"), carve)
u_f(gpu_uniform(p, "u_sea_level"), ter_sea_gen())
u_f4(gpu_uniform(p, "u_isle"), ter_isle_cx, ter_isle_cz, ter_isle_r, ter_isle_fall)
u_f(gpu_uniform(p, "u_isle_mode"), fi(ter_isle_mode))