shader_type spatial; render_mode cull_back, diffuse_burley, specular_schlick_ggx; // Procedural terrain material: no textures to author. Colour comes from world // position + slope + height, layered at several noise scales so the ground // doesn't look like one flat colour from any distance. #include "res://shaders/common.gdshaderinc" uniform sampler2D normal_tex : hint_normal, filter_linear_mipmap, repeat_enable; uniform vec3 grass_lush : source_color = vec3(0.25, 0.40, 0.15); uniform vec3 grass_dry : source_color = vec3(0.48, 0.50, 0.22); uniform vec3 dirt_col : source_color = vec3(0.34, 0.26, 0.17); uniform vec3 rock_light : source_color = vec3(0.50, 0.48, 0.45); uniform vec3 rock_dark : source_color = vec3(0.26, 0.26, 0.27); uniform vec3 sand_col : source_color = vec3(0.62, 0.56, 0.42); uniform vec3 snow_col : source_color = vec3(0.93, 0.95, 0.98); uniform float water_level = -2.6; uniform float snow_height = 40.0; varying vec3 w_pos; varying vec3 w_nrm; void vertex() { w_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; w_nrm = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz); // The terrain mesh has no authored tangents; build a frame for the detail // normal map. (Terrain normals are never parallel to Z, so this is safe.) TANGENT = normalize(cross(NORMAL, vec3(0.0, 0.0, 1.0))); BINORMAL = normalize(cross(TANGENT, NORMAL)); } void fragment() { float slope = 1.0 - clamp(w_nrm.y, 0.0, 1.0); float macro = nz(w_pos.xz * 0.0035); float mid = nz(w_pos.xz * 0.021 + 0.37); float fine = nz(w_pos.xz * 0.17); float micro = nz(w_pos.xz * 0.9); // Grass: lush/dry regions, then mottling at two finer scales. vec3 col = mix(grass_lush, grass_dry, smoothstep(0.48, 0.78, macro + (mid - 0.5) * 0.35)); col *= 0.82 + 0.36 * fine; col *= 0.92 + 0.16 * micro; // Bare dirt patches. float dirt_m = smoothstep(0.64, 0.74, mid + (fine - 0.5) * 0.35); col = mix(col, dirt_col * (0.8 + 0.4 * micro), dirt_m); // Sandy shoreline, darker mud below the waterline. float sand_m = 1.0 - smoothstep(water_level + 0.3, water_level + 1.2 + fine * 0.6, w_pos.y); col = mix(col, sand_col * (0.85 + 0.3 * micro), sand_m); col = mix(col, dirt_col * 0.6, 1.0 - smoothstep(water_level - 1.8, water_level - 0.2, w_pos.y)); // Rock on steep ground, with horizontal strata so cliffs read as layered stone. float tn = tri_nz(w_pos, w_nrm, 0.08); float tn2 = tri_nz(w_pos, w_nrm, 0.45); float strata = sin(w_pos.y * 2.2 + tn * 9.0) * 0.5 + 0.5; vec3 rock = mix(rock_dark, rock_light, clamp(tn * 0.9 + strata * 0.35 - 0.1, 0.0, 1.0)); rock *= 0.82 + 0.36 * tn2; float rock_m = smoothstep(0.28, 0.42, slope + (fine - 0.5) * 0.18); col = mix(col, rock, rock_m); // Snow on the flatter parts of the peaks. float snow_m = smoothstep(snow_height - 3.0, snow_height + 3.0, w_pos.y + (mid - 0.5) * 14.0); snow_m *= 1.0 - smoothstep(0.5, 0.7, slope); col = mix(col, snow_col * (0.92 + 0.08 * fine), snow_m); // Weather: fresh snow settles on flat ground above the waterline; rain // darkens and glosses everything else. float wsnow = snow_mask(w_nrm.y, fine * 0.6 + mid * 0.4) * step(water_level + 0.1, w_pos.y); col = mix(col, snow_col * (0.9 + 0.1 * fine), wsnow); snow_m = max(snow_m, wsnow); float wet = weather_wet * (1.0 - snow_m); col = wet_albedo(col, wet); ALBEDO = col; ROUGHNESS = mix(mix(mix(0.95, 0.82, rock_m), 0.6, snow_m), 0.4, wet * 0.8); SPECULAR = 0.3; NORMAL_MAP = texture(normal_tex, w_pos.xz * 0.35).rgb; NORMAL_MAP_DEPTH = mix(0.5, 1.4, rock_m) * (1.0 - snow_m * 0.7); }