summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 00:44:00 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 00:44:00 +0800
commite9d3e224e802ec41b9f565f5e8ed7eec8470a310 (patch)
tree98247fe11fbf22141e0baaf188ddf29a38e0d9b9 /src/LivingVillage.Desktop
parenta5c3400412fbab11d4f4b83a5d0294292eee5f08 (diff)
downloadliving-village-e9d3e224e802ec41b9f565f5e8ed7eec8470a310.tar.gz
feat(desktop): P17 清晨/黄昏光照精修(暖色天际 + 过渡窗口参数化,分数小时平滑)
Diffstat (limited to 'src/LivingVillage.Desktop')
-rw-r--r--src/LivingVillage.Desktop/M6Presentation.fs69
1 files changed, 51 insertions, 18 deletions
diff --git a/src/LivingVillage.Desktop/M6Presentation.fs b/src/LivingVillage.Desktop/M6Presentation.fs
index 65300f0..11399c0 100644
--- a/src/LivingVillage.Desktop/M6Presentation.fs
+++ b/src/LivingVillage.Desktop/M6Presentation.fs
@@ -13,7 +13,8 @@ type M6RenderProfile =
Background: Color
WorldTint: Color
Blend: float32
- LanternGlow: float32 }
+ LanternGlow: float32
+ SkyWarmth: float32 }
module M6Presentation =
@@ -22,36 +23,68 @@ module M6Presentation =
| Day -> "白天"
| Night -> "夜晚"
- /// Continuous light blend: 0 = full day, 1 = full night. Ramps are deterministic
- /// pure functions of the simulation tick (dawn 4:00-6:00, dusk 20:00-22:00), so the
- /// boundaries agree with Sim.isNightTick (night >= 22:00 or < 6:00).
+ // Transition windows in float hours. Dawn lingers past 6:00 and dusk begins before
+ // 20:00 so the blue hour is longer and the sky warms before it darkens; the anchors
+ // still agree with Sim.isNightTick (night >= 22:00 or < 6:00) once fully dark.
+ let dawnStartHour = 4.0f
+ let dawnEndHour = 6.5f
+ let duskStartHour = 19.5f
+ let duskEndHour = 22.0f
+
+ /// Continuous light blend: 0 = full day, 1 = full night. Deterministic pure function
+ /// of the simulation tick, parameterised by the dawn/dusk windows above.
let lightingBlend (tick: int64) : float32 =
let hourTicks = Sim.ticksPerDay / 24L
- let hour = float32 (tick / hourTicks)
+ let hour = float32 tick / float32 hourTicks
let clamp01 v = if v < 0.0f then 0.0f elif v > 1.0f then 1.0f else v
- if hour >= 6.0f && hour < 20.0f then 0.0f
- elif hour >= 22.0f then 1.0f
- elif hour < 4.0f then 1.0f
- elif hour < 6.0f then clamp01 (1.0f - (hour - 4.0f) / 2.0f)
- else clamp01 ((hour - 20.0f) / 2.0f)
+ if hour >= dawnEndHour && hour < duskStartHour then 0.0f
+ elif hour >= duskEndHour || hour < dawnStartHour then 1.0f
+ elif hour < dawnEndHour then clamp01 (1.0f - (hour - dawnStartHour) / (dawnEndHour - dawnStartHour))
+ else clamp01 ((hour - duskStartHour) / (duskEndHour - duskStartHour))
+
+ /// Golden-hour warmth (0..1): a triangular bump peaking mid-dawn and mid-dusk, zero at
+ /// noon and midnight. Drives the warm sky/world tint so P17 transition frames read golden.
+ let transitionWarmth (tick: int64) : float32 =
+ let hour = float32 tick / float32 (Sim.ticksPerDay / 24L)
+ let bump (startHour: float32) (endHour: float32) : float32 =
+ if hour <= startHour || hour >= endHour then
+ 0.0f
+ else
+ let mid = (startHour + endHour) * 0.5f
+ let half = (endHour - startHour) * 0.5f
+ 1.0f - abs (hour - mid) / half
+ max (bump dawnStartHour dawnEndHour) (bump duskStartHour duskEndHour)
let private lerp (a: int) (b: int) (blend: float32) : int =
a + int (float32 (b - a) * blend)
- let backgroundAtBlend (blend: float32) : Color =
- Color(lerp 100 18 blend, lerp 160 25 blend, lerp 210 55 blend)
+ /// Blended sky/world colour plus the golden-hour warmth shift (warm channels up, blue down).
+ let backgroundAtWarmth (blend: float32) (warmth: float32) : Color =
+ Color(
+ lerp 100 18 blend + int (52.0f * warmth),
+ lerp 160 25 blend + int (14.0f * warmth),
+ lerp 210 55 blend - int (34.0f * warmth))
+
+ let worldTintAtWarmth (blend: float32) (warmth: float32) : Color =
+ Color(
+ lerp 255 118 blend + int (10.0f * warmth),
+ lerp 248 136 blend - int (6.0f * warmth),
+ lerp 224 184 blend - int (26.0f * warmth))
- let worldTintAtBlend (blend: float32) : Color =
- Color(lerp 255 118 blend, lerp 248 136 blend, lerp 224 184 blend)
+ /// Warmth-free base colours (kept for callers that only have a blend value).
+ let backgroundAtBlend (blend: float32) : Color = backgroundAtWarmth blend 0.0f
+ let worldTintAtBlend (blend: float32) : Color = worldTintAtWarmth blend 0.0f
- /// Pure day-phase lighting: deterministic tint/background plus lantern warm-glow boost.
+ /// Pure day-phase lighting: deterministic tint/background/warmth plus lantern glow.
let lightingAtTick (tick: int64) : M6RenderProfile =
let blend = lightingBlend tick
+ let warmth = transitionWarmth tick
{ Mode = if blend < 0.5f then Day else Night
- Background = backgroundAtBlend blend
+ Background = backgroundAtWarmth blend warmth
Blend = blend
- WorldTint = worldTintAtBlend blend
- LanternGlow = blend }
+ WorldTint = worldTintAtWarmth blend warmth
+ LanternGlow = blend
+ SkyWarmth = warmth }
let profileAtTick (tick: int64) : M6RenderProfile = lightingAtTick tick