diff --git a/packages/ludic.render3d/gpu_vk_res.ludic b/packages/ludic.render3d/gpu_vk_res.ludic index efa903eb..8a401bea 100644 --- a/packages/ludic.render3d/gpu_vk_res.ludic +++ b/packages/ludic.render3d/gpu_vk_res.ludic @@ -518,6 +518,28 @@ function gvk_layer_views_drop(render3d_st: mut Render3dState, tex: int) -> void while len(texs) > w { List.pop(texs) } } +# the per-level and per-layer views gvk_view_of made of a texture go with it: kept, each held its +# old Metal texture alive, so every rebuild of the screen targets left the last set's memory behind +function gvk_layer_views_drop(render3d_st: mut Render3dState, tex: int) -> void { + if render3d_st.gvk_layer_views == null { return } + let keys = render3d_st.gvk_layer_views + let views = render3d_st.gvk_layer_view + let texs = render3d_st.gvk_layer_view_tex + var w = 0 + for i in 0 .. len(keys) { + if texs[i] == tex { + render3d_st.gvk_mk_x_view += 1 + Vk.destroy_image_view(render3d_st.gvk_dev, views[i], null) + } else { + keys[w] = keys[i]; views[w] = views[i]; texs[w] = texs[i] + w += 1 + } + } + while len(keys) > w { List.pop(keys) } + while len(views) > w { List.pop(views) } + while len(texs) > w { List.pop(texs) } +} + # ---- samplers ----------------------------------------------------------------------------- # One VkSampler per distinct way of reading a texture, made the first time it is asked for. # The arguments are what the renderer set through gpu_tex_param (0 where it set nothing, which diff --git a/packages/ludic.render3d/shaders/grass.vert b/packages/ludic.render3d/shaders/grass.vert index a8ec80b4..bc9f9292 100644 --- a/packages/ludic.render3d/shaders/grass.vert +++ b/packages/ludic.render3d/shaders/grass.vert @@ -158,7 +158,7 @@ vec3 bladeField(vec2 xz, float y) { // dry and green patches a few metres across, and the tussocks' own shade of the same green float dryp = smoothstep(0.2, 0.8, gnoise(xz * 0.33 + 17.0) * 0.5 + 0.5); t *= mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); - return t * (0.72 + 0.56 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); + return t * (0.55 + 0.80 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); } void main() { diff --git a/packages/ludic.render3d/shaders/grass_cull.comp b/packages/ludic.render3d/shaders/grass_cull.comp index 442882bb..c246c027 100644 --- a/packages/ludic.render3d/shaders/grass_cull.comp +++ b/packages/ludic.render3d/shaders/grass_cull.comp @@ -132,7 +132,7 @@ vec3 bladeField(vec2 xz, float y) { t *= mix(vec3(0.7, 0.8, 0.55), vec3(1.1, 1.05, 0.85), patchy); float dryp = smoothstep(0.2, 0.8, gnoise(xz * 0.33 + 17.0) * 0.5 + 0.5); t *= mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); - return t * (0.72 + 0.56 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); + return t * (0.55 + 0.80 * (gnoise(xz * 1.9 + 41.0) * 0.5 + 0.5)); } void main() { diff --git a/packages/ludic.render3d/shaders/terrain.frag b/packages/ludic.render3d/shaders/terrain.frag index f010880f..d1520f4d 100644 --- a/packages/ludic.render3d/shaders/terrain.frag +++ b/packages/ludic.render3d/shaders/terrain.frag @@ -87,6 +87,7 @@ vec3 terNormal(vec2 uv) { vec2 xz = terNormalXZ(uv); return vec3(xz.x, sqrt(max uniform sampler2D u_rock_d; uniform sampler2D u_rock_n; uniform sampler2D u_rock_a; uniform sampler2D u_snow_d; uniform sampler2D u_carpet; // the clump cards baked straight down (alpha = coverage) uniform float u_carpet_on; +uniform float u_grass_reach; // where the GPU blades stop (0: none): the ground under them is in their shade uniform float u_snow_line; uniform float u_lake_level; // the ground just above the water is wet and dark // ---- RAIN THAT LEAVES SOMETHING BEHIND ----------------------------------------------------- @@ -410,6 +411,18 @@ vec3 groundShade(vec3 p, vec3 N, float slope, float dist, float viewDepth, bool vec3 cc = cp.rgb * vec3(0.5, 0.57, 0.45) * mix(vec3(0.85, 0.92, 0.9), vec3(1.1, 1.05, 0.85), smoothstep(-0.35, 0.35, N.z)) * (0.75 + 0.35 * lush); gA = mix(gA, cc, max(cp.a, 0.35) * carpetW * 0.97); } + // The blades' own patches (grass_cull.comp's bladeField): patchiness and dry patches, so where + // the blades thin out the ground they stand on carries the same meadow, not a lawn + { + float patchy = fbmC(cheap, p.xz * 0.045, 3) * 0.5 + 0.5; + float dryp = smoothstep(0.2, 0.8, gnoise(p.xz * 0.33 + 17.0) * 0.5 + 0.5); + vec3 pf = mix(vec3(0.7, 0.8, 0.55), vec3(1.1, 1.05, 0.85), patchy) * mix(vec3(0.92, 1.0, 0.95), vec3(1.30, 1.10, 0.62), dryp); + // and the tussocks' own shade, half a metre across: where blades stand a pixel wide the ground + // is most of what shows, and it has to carry the same light and dark as the blades on it + float tus = 0.55 + 0.80 * (gnoise(p.xz * 1.9 + 41.0) * 0.5 + 0.5); + gA *= mix(vec3(1.0), pf / vec3(0.95, 0.96, 0.72) * tus, grassW); + if (u_grass_reach > 0.0) gA *= mix(1.0, mix(0.50, 1.0, smoothstep(u_grass_reach * 0.25, u_grass_reach, dist)), grassW); + } // Subalpine forest floor: dark duff where the stands are dense. The photograph-driven // term marks duff only where the survey actually shows dense conifer, and is the same // at any distance — ground shading must not depend on where the viewer is. diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 9483c73a..337c66fb 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -493,6 +493,9 @@ function terrain_bind_prog(render3d_st: mut Render3dState, p: int) -> void { r3d_bind_2d(render3d_st, p, "u_snow_d", 10, render3d_st.ter_tex[9]) if render3d_st.ter_carpet != 0 { r3d_bind_2d(render3d_st, p, "u_carpet", 11, render3d_st.ter_carpet); u_f(render3d_st, gpu_uniform(render3d_st, p, "u_carpet_on"), 1.0) } else { r3d_bind_2d(render3d_st, p, "u_carpet", 11, render3d_st.ter_tex[0]); u_f(render3d_st, gpu_uniform(render3d_st, p, "u_carpet_on"), 0.0) } + var reach = 0.0 + if grass_live(render3d_st) { reach = render3d_st.grass_radius } + u_f(render3d_st, gpu_uniform(render3d_st, p, "u_grass_reach"), reach) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_half"), float(render3d_st.TERRAIN_HALF)) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_texel"), 1.0 / float(TERRAIN_RES)) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_snow_line"), render3d_st.ter_snow_line)