Files
Cinderwild/shaders/terrain.gdshader
Paul Bailey 12f7fd677d M0.5: detailed scenery, mountain/sea-cliff boundary, Godot 4.7
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.
2026-11-03 23:49:39 +00:00

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);
}