Scenery (all procedural, seeded, no imported assets): - Terrain shader: grass/dry grass/dirt/sand/rock strata/snow + detail normals - 5 tree types, faceted mossy rocks, cliff outcrops, sea stacks, bushes, logs and stumps, batched as MultiMesh per 48 m cell - Wind-animated grass and flowers streamed in tiles around the player - Lake (depth-shaded water with shore foam), ocean to the horizon, cloud sky shader, volumetric fog, glow, softer shadows - Swimming: float at the surface, swim-jump to climb out World bounds: - Steep mountain rim with an invisible wall partway up; one side opens to a sheer sea cliff with the wall 1 m inside the edge; whole chunk is an island so the mesh edge is never visible - Respawn safety net if the player leaves the walkable area or the map - Fix: the cliff edge now tracks the wall in the headlands (it used to drop away inside the wall, leaving a pit with lake water hanging over it) Tests: tests/test_boundary.gd (24-direction escape attempt) and tests/test_terrain_edges.gd (no ground below waterline near the wall). Upgrades the project to Godot 4.7 (project.godot rewritten by the 4.7 importer, .uid files added) and documents it in the setup guide.
75 lines
3.1 KiB
Plaintext
75 lines
3.1 KiB
Plaintext
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);
|
|
|
|
ALBEDO = col;
|
|
ROUGHNESS = mix(mix(0.95, 0.82, rock_m), 0.6, snow_m);
|
|
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);
|
|
}
|