Merge grass/far into main (gathering every branch; main's side kept on conflicts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 17:21:20 +03:00
commit e72a4d0a13
5 changed files with 40 additions and 2 deletions

View file

@ -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

View file

@ -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() {

View file

@ -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() {

View file

@ -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.

View file

@ -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)