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.
48 lines
1.7 KiB
Plaintext
48 lines
1.7 KiB
Plaintext
shader_type spatial;
|
|
render_mode blend_mix, cull_disabled, diffuse_burley, specular_schlick_ggx;
|
|
|
|
// Lake water: two scrolling normal maps for ripples, colour and opacity from
|
|
// the depth of water under each pixel (clear shallows, dark deep water), and
|
|
// foam where the surface meets the shore.
|
|
|
|
#include "res://shaders/common.gdshaderinc"
|
|
|
|
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
|
|
uniform sampler2D normal_tex : hint_normal, filter_linear_mipmap, repeat_enable;
|
|
uniform vec3 shallow_col : source_color = vec3(0.22, 0.44, 0.42);
|
|
uniform vec3 deep_col : source_color = vec3(0.03, 0.12, 0.17);
|
|
uniform float clarity = 2.5;
|
|
|
|
varying vec3 w_pos;
|
|
|
|
void vertex() {
|
|
w_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
}
|
|
|
|
void fragment() {
|
|
vec2 uv = w_pos.xz;
|
|
vec3 n1 = texture(normal_tex, uv * 0.045 + TIME * vec2(0.012, 0.008)).rgb;
|
|
vec3 n2 = texture(normal_tex, uv * 0.11 + TIME * vec2(-0.010, 0.017)).rgb;
|
|
NORMAL_MAP = mix(n1, n2, 0.5);
|
|
NORMAL_MAP_DEPTH = 0.7;
|
|
|
|
// Thickness of water between the surface and whatever is behind it.
|
|
// (Godot 4.3+ uses reversed-Z: raw depth is the NDC z.)
|
|
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r;
|
|
vec4 behind = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0);
|
|
behind.xyz /= behind.w;
|
|
float thick = max(VERTEX.z - behind.z, 0.0);
|
|
|
|
float t = 1.0 - exp(-thick / clarity);
|
|
vec3 col = mix(shallow_col, deep_col, t);
|
|
float foam_n = nz(uv * 0.6 + TIME * 0.02);
|
|
float foam = (1.0 - smoothstep(0.0, 0.25 + foam_n * 0.35, thick)) * 0.8;
|
|
col = mix(col, vec3(0.88, 0.93, 0.93), foam);
|
|
|
|
ALBEDO = col;
|
|
ALPHA = clamp(mix(0.3, 0.93, t) + foam, 0.0, 1.0);
|
|
ROUGHNESS = 0.04;
|
|
SPECULAR = 0.6;
|
|
METALLIC = 0.0;
|
|
}
|