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 study desk, and rest bench now have compact presentation props that remain
readable in the runtime cinematic frame. 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: Next:
1. Strengthen water and foreground silhouettes without adding new simulation 1. Continue growing resource discovery with finite ResourceNode instances
mechanics. 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 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 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. authored path strips that reveal the active village task loop.
- Landmark and work-site silhouette props make the ridge landmark, pantry, - Landmark and work-site silhouette props make the ridge landmark, pantry,
guard, study, and rest sites easier to identify without debug labels. 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 This order strengthens the simulation while regularly producing visible
progress suitable for public development updates. 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"] [sub_resource type="StandardMaterial3D" id="Material_keep"]
albedo_color = Color(0.45, 0.42, 0.38, 1) albedo_color = Color(0.45, 0.42, 0.38, 1)
@@ -6,15 +6,15 @@ roughness = 0.85
[sub_resource type="BoxMesh" id="Mesh_keep"] [sub_resource type="BoxMesh" id="Mesh_keep"]
material = SubResource("Material_keep") material = SubResource("Material_keep")
size = Vector3(6, 6, 8) size = Vector3(7, 6, 9)
[sub_resource type="StandardMaterial3D" id="Material_parapet"] [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 roughness = 0.85
[sub_resource type="BoxMesh" id="Mesh_parapet"] [sub_resource type="BoxMesh" id="Mesh_parapet"]
material = SubResource("Material_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"] [sub_resource type="StandardMaterial3D" id="Material_turret"]
albedo_color = Color(0.45, 0.42, 0.38, 1) albedo_color = Color(0.45, 0.42, 0.38, 1)
@@ -26,6 +26,28 @@ top_radius = 0.6
bottom_radius = 0.7 bottom_radius = 0.7
height = 4.0 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="FortressBlockout" type="Node3D"]
[node name="Keep" type="MeshInstance3D" parent="."] [node name="Keep" type="MeshInstance3D" parent="."]
@@ -36,6 +58,62 @@ mesh = SubResource("Mesh_keep")
position = Vector3(0, 6.75, 0) position = Vector3(0, 6.75, 0)
mesh = SubResource("Mesh_parapet") mesh = SubResource("Mesh_parapet")
[node name="Turret" type="MeshInstance3D" parent="."] [node name="Crenellation_01" type="MeshInstance3D" parent="."]
position = Vector3(2.8, 2.0, 3.5) 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") 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="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="WaterRoot" type="Node3D" parent="."]
[node name="RiverSurface" parent="WaterRoot" instance=ExtResource("9_water")] [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"] [sub_resource type="BoxMesh" id="Mesh_water_body"]
material = SubResource("ShaderMaterial_water") material = SubResource("ShaderMaterial_water")
size = Vector3(5, 0.08, 56) size = Vector3(8, 0.08, 58)
[node name="WaterBody" type="Node3D"] [node name="WaterBody" type="Node3D"]
position = Vector3(25, 0.12, 0) position = Vector3(25, 0.08, 0)
[node name="Mesh" type="MeshInstance3D" parent="."] [node name="Mesh" type="MeshInstance3D" parent="."]
mesh = SubResource("Mesh_water_body") 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"] [sub_resource type="BoxMesh" id="Mesh_waterfall_body"]
material = SubResource("ShaderMaterial_waterfall") material = SubResource("ShaderMaterial_waterfall")
size = Vector3(5, 10, 0.5) size = Vector3(8, 12, 0.5)
[node name="WaterfallBody" type="Node3D"] [node name="WaterfallBody" type="Node3D"]
position = Vector3(25, 5, -24) position = Vector3(25, 6, -24)
[node name="Mesh" type="MeshInstance3D" parent="."] [node name="Mesh" type="MeshInstance3D" parent="."]
mesh = SubResource("Mesh_waterfall_body") mesh = SubResource("Mesh_waterfall_body")
+13 -13
View File
@@ -1,19 +1,19 @@
[gd_scene load_steps=4 format=3] [gd_scene load_steps=4 format=3]
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"]
lifetime_randomness = 0.2 lifetime_randomness = 0.35
spread = 180.0 spread = 180.0
gravity = Vector3(0, -0.5, 0) gravity = Vector3(0, -0.35, 0)
initial_velocity_min = 0.3 initial_velocity_min = 0.25
initial_velocity_max = 0.8 initial_velocity_max = 1.2
scale_min = 0.1 scale_min = 0.08
scale_max = 1.5 scale_max = 2.0
color = Color(0.85, 0.95, 0.95, 0.4) color = Color(0.82, 0.94, 0.96, 0.35)
emission_shape = 1 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"] [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 transparency = 1
shading_mode = 0 shading_mode = 0
billboard_mode = 1 billboard_mode = 1
@@ -21,14 +21,14 @@ cull_mode = 0
[sub_resource type="QuadMesh" id="Mesh_mist"] [sub_resource type="QuadMesh" id="Mesh_mist"]
material = SubResource("Material_mist") material = SubResource("Material_mist")
size = Vector2(1.5, 1.5) size = Vector2(1.8, 1.8)
[node name="WaterfallMist" type="GPUParticles3D"] [node name="WaterfallMist" type="GPUParticles3D"]
emitting = true emitting = true
amount = 48 amount = 72
lifetime = 2.5 lifetime = 3.0
one_shot = false one_shot = false
preprocess = 1.0 preprocess = 1.5
speed_scale = 1.0 speed_scale = 1.0
local_coords = true local_coords = true
draw_order = 0 draw_order = 0
+43 -9
View File
@@ -1,20 +1,54 @@
shader_type spatial; shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; 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_strength : hint_range(0.0, 0.5) = 0.08;
uniform float wave_speed : hint_range(0.0, 5.0) = 1.2; 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() { void vertex() {
vec3 pos = VERTEX; vec3 pos = VERTEX;
float wave = sin(pos.z * 0.4 + TIME * wave_speed) * wave_strength; float wave_a = sin(pos.x * 0.35 + pos.z * 0.25 + TIME * wave_speed) * wave_strength;
pos.y += wave; 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; VERTEX = pos;
} }
void fragment() { void fragment() {
ALBEDO = vec3(0.12, 0.39, 0.55); vec3 view_dir = VIEW;
METALLIC = 0.15; vec3 normal = NORMAL;
ROUGHNESS = 0.1; vec3 view_norm = normalize(view_dir);
EMISSION = vec3(0.05, 0.19, 0.24) * 0.45;
ALPHA = 0.72; 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; shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; 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() { void vertex() {
} }
@@ -9,13 +14,37 @@ void vertex() {
void fragment() { void fragment() {
vec2 uv = UV; vec2 uv = UV;
uv.y += TIME * scroll_speed; uv.y += TIME * scroll_speed;
float pattern = fract(uv.y * 4.0);
pattern = smoothstep(0.3, 0.7, pattern); float band_a = fract(uv.y * 5.0 + sin(uv.x * 2.0) * 0.15);
pattern = mix(0.4, 1.0, pattern); band_a = smoothstep(0.2, 0.55, band_a) * smoothstep(0.8, 0.45, band_a);
vec3 base_color = mix(vec3(0.6, 0.85, 0.9), vec3(1.0, 1.0, 1.0), pattern);
ALBEDO = base_color; float band_b = fract(uv.y * 3.5 + cos(uv.x * 2.5 + TIME * 0.3) * 0.12);
ALPHA = mix(0.3, 0.7, pattern); band_b = smoothstep(0.1, 0.5, band_b) * smoothstep(0.85, 0.4, band_b);
EMISSION = vec3(0.36, 0.52, 0.54) * 0.35 * pattern;
METALLIC = 0.0; float foam_a = fract(uv.y * 4.0);
ROUGHNESS = 0.2; 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);
} }