feat: strengthen water and foreground silhouettes

- Upgrade river shader with multi-layer UV flow, fresnel edges, foam highlights, and specular sparkle
- Upgrade waterfall shader with dual-band scrolling, distinct foam contrast, and rim transparency
- Widen river (8m) and waterfall (8m) for greater visual presence
- Increase waterfall mist particles to 72 with wider spread and longer lifetime
- Add fortress crenellations (10), three turrets, and prominent ridge banner for readable skyline silhouette
- Remove duplicate banner nodes from JajceWorld instance (now owned by FortressBlockout scene)
- Update BUILD_IN_PUBLIC_PLAN and LEARNING_ROADMAP to reflect completion
This commit is contained in:
2026-07-08 17:17:34 +02:00
parent c6e2d64162
commit 4054e2c0dd
9 changed files with 200 additions and 52 deletions
+13 -2
View File
@@ -644,10 +644,21 @@ Completed:
study desk, and rest bench now have compact presentation props that remain
readable in the runtime cinematic frame.
Completed:
15. Water and foreground silhouette strengthening: river/waterfall shaders
now include multi-layer UV flow, fresnel edges, foam highlights, sparkle
specular, and better color depth; the waterfall has distinct foam bands
and rim transparency; mist particle count and spread increased; the river
and waterfall widened; the fortress ridge landmark now has crenellations,
a dedicated banner pole with larger banner, and three turrets for a more
readable silhouette against the sky.
Next:
1. Strengthen water and foreground silhouettes without adding new simulation
mechanics.
1. Continue growing resource discovery with finite ResourceNode instances
placed in meaningful contexts (from LEARNING_ROADMAP). See the roadmap
for the next systems priority.
Do not start with GIS data, a full city, a large asset pack, or more NPC
mechanics. The next proof is a beautiful stage for the systems that already
+4
View File
@@ -689,6 +689,10 @@ Recently completed:
authored path strips that reveal the active village task loop.
- Landmark and work-site silhouette props make the ridge landmark, pantry,
guard, study, and rest sites easier to identify without debug labels.
- Water and foreground silhouettes strengthened: river/waterfall shaders with
multi-layer UV flow, fresnel edges, foam highlights, and sparkle; wider river
and waterfall; fortress with crenellations, three turrets, and prominent
ridge banner.
This order strengthens the simulation while regularly producing visible
progress suitable for public development updates.
+84 -6
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3]
[gd_scene load_steps=12 format=3]
[sub_resource type="StandardMaterial3D" id="Material_keep"]
albedo_color = Color(0.45, 0.42, 0.38, 1)
@@ -6,15 +6,15 @@ roughness = 0.85
[sub_resource type="BoxMesh" id="Mesh_keep"]
material = SubResource("Material_keep")
size = Vector3(6, 6, 8)
size = Vector3(7, 6, 9)
[sub_resource type="StandardMaterial3D" id="Material_parapet"]
albedo_color = Color(0.45, 0.42, 0.38, 1)
albedo_color = Color(0.42, 0.39, 0.35, 1)
roughness = 0.85
[sub_resource type="BoxMesh" id="Mesh_parapet"]
material = SubResource("Material_parapet")
size = Vector3(5, 1.5, 7)
size = Vector3(6.6, 1.5, 7.8)
[sub_resource type="StandardMaterial3D" id="Material_turret"]
albedo_color = Color(0.45, 0.42, 0.38, 1)
@@ -26,6 +26,28 @@ top_radius = 0.6
bottom_radius = 0.7
height = 4.0
[sub_resource type="BoxMesh" id="Mesh_crenellation"]
material = SubResource("Material_parapet")
size = Vector3(0.55, 0.9, 0.7)
[sub_resource type="StandardMaterial3D" id="Material_pole"]
albedo_color = Color(0.24, 0.13, 0.07, 1)
roughness = 0.82
[sub_resource type="CylinderMesh" id="Mesh_pole"]
material = SubResource("Material_pole")
top_radius = 0.08
bottom_radius = 0.1
height = 3.5
[sub_resource type="StandardMaterial3D" id="Material_banner"]
albedo_color = Color(0.68, 0.12, 0.1, 1)
roughness = 0.65
[sub_resource type="BoxMesh" id="Mesh_banner"]
material = SubResource("Material_banner")
size = Vector3(0.14, 2.0, 1.25)
[node name="FortressBlockout" type="Node3D"]
[node name="Keep" type="MeshInstance3D" parent="."]
@@ -36,6 +58,62 @@ mesh = SubResource("Mesh_keep")
position = Vector3(0, 6.75, 0)
mesh = SubResource("Mesh_parapet")
[node name="Turret" type="MeshInstance3D" parent="."]
position = Vector3(2.8, 2.0, 3.5)
[node name="Crenellation_01" type="MeshInstance3D" parent="."]
position = Vector3(-2.8, 7.7, -3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_02" type="MeshInstance3D" parent="."]
position = Vector3(-1.4, 7.7, -3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_03" type="MeshInstance3D" parent="."]
position = Vector3(0, 7.7, -3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_04" type="MeshInstance3D" parent="."]
position = Vector3(1.4, 7.7, -3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_05" type="MeshInstance3D" parent="."]
position = Vector3(2.8, 7.7, -3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_06" type="MeshInstance3D" parent="."]
position = Vector3(-2.8, 7.7, 3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_07" type="MeshInstance3D" parent="."]
position = Vector3(-1.4, 7.7, 3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_08" type="MeshInstance3D" parent="."]
position = Vector3(0, 7.7, 3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_09" type="MeshInstance3D" parent="."]
position = Vector3(1.4, 7.7, 3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Crenellation_10" type="MeshInstance3D" parent="."]
position = Vector3(2.8, 7.7, 3.1)
mesh = SubResource("Mesh_crenellation")
[node name="Turret_A" type="MeshInstance3D" parent="."]
position = Vector3(-3.2, 2.0, -4)
mesh = SubResource("Mesh_turret")
[node name="Turret_B" type="MeshInstance3D" parent="."]
position = Vector3(3.2, 2.0, -4)
mesh = SubResource("Mesh_turret")
[node name="Turret_C" type="MeshInstance3D" parent="."]
position = Vector3(3.2, 2.0, 4)
mesh = SubResource("Mesh_turret")
[node name="RidgeBannerPole" type="MeshInstance3D" parent="."]
position = Vector3(0, 8.4, 3.8)
mesh = SubResource("Mesh_pole")
[node name="RidgeBanner" type="MeshInstance3D" parent="."]
position = Vector3(0, 9.2, 4.45)
mesh = SubResource("Mesh_banner")
-8
View File
@@ -186,14 +186,6 @@ position = Vector3(-25, 7, 9)
[node name="Keep" parent="FortressBlockout" instance=ExtResource("5_fortress")]
[node name="RidgeBannerPole" type="MeshInstance3D" parent="FortressBlockout/Keep"]
position = Vector3(2.2, 8.6, 2.8)
mesh = SubResource("Mesh_site_pole")
[node name="RidgeBanner" type="MeshInstance3D" parent="FortressBlockout/Keep"]
position = Vector3(2.2, 8.95, 3.35)
mesh = SubResource("Mesh_landmark_banner")
[node name="WaterRoot" type="Node3D" parent="."]
[node name="RiverSurface" parent="WaterRoot" instance=ExtResource("9_water")]
+2 -2
View File
@@ -7,10 +7,10 @@ shader = ExtResource("1_water_shader")
[sub_resource type="BoxMesh" id="Mesh_water_body"]
material = SubResource("ShaderMaterial_water")
size = Vector3(5, 0.08, 56)
size = Vector3(8, 0.08, 58)
[node name="WaterBody" type="Node3D"]
position = Vector3(25, 0.12, 0)
position = Vector3(25, 0.08, 0)
[node name="Mesh" type="MeshInstance3D" parent="."]
mesh = SubResource("Mesh_water_body")
+2 -2
View File
@@ -7,10 +7,10 @@ shader = ExtResource("1_waterfall_shader")
[sub_resource type="BoxMesh" id="Mesh_waterfall_body"]
material = SubResource("ShaderMaterial_waterfall")
size = Vector3(5, 10, 0.5)
size = Vector3(8, 12, 0.5)
[node name="WaterfallBody" type="Node3D"]
position = Vector3(25, 5, -24)
position = Vector3(25, 6, -24)
[node name="Mesh" type="MeshInstance3D" parent="."]
mesh = SubResource("Mesh_waterfall_body")
+13 -13
View File
@@ -1,19 +1,19 @@
[gd_scene load_steps=4 format=3]
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"]
lifetime_randomness = 0.2
lifetime_randomness = 0.35
spread = 180.0
gravity = Vector3(0, -0.5, 0)
initial_velocity_min = 0.3
initial_velocity_max = 0.8
scale_min = 0.1
scale_max = 1.5
color = Color(0.85, 0.95, 0.95, 0.4)
gravity = Vector3(0, -0.35, 0)
initial_velocity_min = 0.25
initial_velocity_max = 1.2
scale_min = 0.08
scale_max = 2.0
color = Color(0.82, 0.94, 0.96, 0.35)
emission_shape = 1
emission_box_extents = Vector3(2.5, 1.0, 0.5)
emission_box_extents = Vector3(4.5, 1.5, 1.0)
[sub_resource type="StandardMaterial3D" id="Material_mist"]
albedo_color = Color(0.85, 0.95, 0.95, 0.35)
albedo_color = Color(0.82, 0.94, 0.96, 0.28)
transparency = 1
shading_mode = 0
billboard_mode = 1
@@ -21,14 +21,14 @@ cull_mode = 0
[sub_resource type="QuadMesh" id="Mesh_mist"]
material = SubResource("Material_mist")
size = Vector2(1.5, 1.5)
size = Vector2(1.8, 1.8)
[node name="WaterfallMist" type="GPUParticles3D"]
emitting = true
amount = 48
lifetime = 2.5
amount = 72
lifetime = 3.0
one_shot = false
preprocess = 1.0
preprocess = 1.5
speed_scale = 1.0
local_coords = true
draw_order = 0
+43 -9
View File
@@ -1,20 +1,54 @@
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform float wave_strength : hint_range(0.0, 1.0) = 0.15;
uniform float wave_speed : hint_range(0.0, 5.0) = 1.2;
uniform float wave_strength : hint_range(0.0, 0.5) = 0.08;
uniform float wave_speed : hint_range(0.0, 5.0) = 0.8;
uniform float flow_speed : hint_range(0.0, 3.0) = 0.6;
uniform vec3 deep_color : source_color = vec3(0.06, 0.24, 0.38);
uniform vec3 shallow_color : source_color = vec3(0.18, 0.44, 0.58);
uniform vec3 foam_color : source_color = vec3(0.82, 0.88, 0.86);
uniform float rim_power : hint_range(0.5, 5.0) = 2.5;
uniform float sparkle_strength : hint_range(0.0, 1.0) = 0.12;
global uniform vec3 sun_direction = vec3(0.4, 0.8, 0.4);
void vertex() {
vec3 pos = VERTEX;
float wave = sin(pos.z * 0.4 + TIME * wave_speed) * wave_strength;
pos.y += wave;
float wave_a = sin(pos.x * 0.35 + pos.z * 0.25 + TIME * wave_speed) * wave_strength;
float wave_b = cos(pos.x * 0.45 - pos.z * 0.55 + TIME * wave_speed * 0.7) * wave_strength * 0.6;
float wave_c = sin(pos.z * 0.7 + TIME * wave_speed * 1.3) * wave_strength * 0.35;
pos.y += wave_a + wave_b + wave_c;
VERTEX = pos;
}
void fragment() {
ALBEDO = vec3(0.12, 0.39, 0.55);
METALLIC = 0.15;
ROUGHNESS = 0.1;
EMISSION = vec3(0.05, 0.19, 0.24) * 0.45;
ALPHA = 0.72;
vec3 view_dir = VIEW;
vec3 normal = NORMAL;
vec3 view_norm = normalize(view_dir);
float rim = 1.0 - abs(dot(normal, view_norm));
rim = pow(rim, rim_power);
rim = smoothstep(0.15, 0.7, rim);
vec2 uv = UV;
uv.x += TIME * flow_speed * 0.4;
uv.y += TIME * flow_speed * 0.3;
float flow_a = sin(uv.x * 8.0 + uv.y * 5.0) * 0.5 + 0.5;
float flow_b = cos(uv.y * 6.0 - uv.x * 4.0 + TIME * 0.25) * 0.5 + 0.5;
float flow = mix(flow_a, flow_b, 0.5);
vec3 water_color = mix(deep_color, shallow_color, flow * 0.6 + 0.2);
water_color = mix(water_color, foam_color, rim * 0.65);
vec3 half_vec = normalize(view_norm + normalize(sun_direction));
float specular = pow(max(dot(normal, half_vec), 0.0), 120.0);
specular *= smoothstep(0.4, 0.85, rim);
float sparkle = specular * sparkle_strength;
ALBEDO = water_color;
METALLIC = 0.08;
ROUGHNESS = 0.22;
EMISSION = water_color * 0.04 + foam_color * sparkle;
ALPHA = 0.76;
ALPHA_SCISSOR_THRESHOLD = 0.0;
}
+39 -10
View File
@@ -1,7 +1,12 @@
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform float scroll_speed : hint_range(0.0, 5.0) = 2.0;
uniform float scroll_speed : hint_range(0.0, 5.0) = 1.8;
uniform vec3 deep_color : source_color = vec3(0.1, 0.45, 0.55);
uniform vec3 mid_color : source_color = vec3(0.45, 0.78, 0.82);
uniform vec3 foam_color : source_color = vec3(0.9, 0.95, 0.95);
uniform float foam_contrast : hint_range(0.5, 4.0) = 2.2;
uniform float sparkle_strength : hint_range(0.0, 0.8) = 0.25;
void vertex() {
}
@@ -9,13 +14,37 @@ void vertex() {
void fragment() {
vec2 uv = UV;
uv.y += TIME * scroll_speed;
float pattern = fract(uv.y * 4.0);
pattern = smoothstep(0.3, 0.7, pattern);
pattern = mix(0.4, 1.0, pattern);
vec3 base_color = mix(vec3(0.6, 0.85, 0.9), vec3(1.0, 1.0, 1.0), pattern);
ALBEDO = base_color;
ALPHA = mix(0.3, 0.7, pattern);
EMISSION = vec3(0.36, 0.52, 0.54) * 0.35 * pattern;
METALLIC = 0.0;
ROUGHNESS = 0.2;
float band_a = fract(uv.y * 5.0 + sin(uv.x * 2.0) * 0.15);
band_a = smoothstep(0.2, 0.55, band_a) * smoothstep(0.8, 0.45, band_a);
float band_b = fract(uv.y * 3.5 + cos(uv.x * 2.5 + TIME * 0.3) * 0.12);
band_b = smoothstep(0.1, 0.5, band_b) * smoothstep(0.85, 0.4, band_b);
float foam_a = fract(uv.y * 4.0);
foam_a = pow(smoothstep(0.0, 0.12, foam_a) * smoothstep(0.3, 0.15, foam_a), foam_contrast);
float foam_b = fract(uv.y * 6.0 + uv.x * 1.5);
foam_b = pow(smoothstep(0.5, 0.58, foam_b) * smoothstep(0.7, 0.6, foam_b), foam_contrast * 0.8);
float foam = foam_a * 0.7 + foam_b * 0.3;
vec3 base = mix(deep_color, mid_color, band_a * 0.5 + band_b * 0.3);
vec3 color = mix(base, foam_color, foam);
float sparkle = pow(foam_a, 3.0) * sparkle_strength;
float highlight = band_a * band_b * 0.35;
float emission_value = sparkle + highlight * 0.3;
ALBEDO = color;
ALPHA = mix(0.25, 0.78, foam * 0.7 + 0.15);
EMISSION = color * emission_value;
METALLIC = 0.05;
ROUGHNESS = 0.15;
vec3 normal = NORMAL;
vec3 view_dir = VIEW;
vec3 view_norm = normalize(view_dir);
float rim = 1.0 - abs(dot(normal, view_norm));
ALPHA = mix(ALPHA, ALPHA * 0.5, rim * 0.6);
}