diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:44:00 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:44:00 +0800 |
| commit | e9d3e224e802ec41b9f565f5e8ed7eec8470a310 (patch) | |
| tree | 98247fe11fbf22141e0baaf188ddf29a38e0d9b9 /src/LivingVillage.Desktop | |
| parent | a5c3400412fbab11d4f4b83a5d0294292eee5f08 (diff) | |
| download | living-village-e9d3e224e802ec41b9f565f5e8ed7eec8470a310.tar.gz | |
feat(desktop): P17 清晨/黄昏光照精修(暖色天际 + 过渡窗口参数化,分数小时平滑)
Diffstat (limited to 'src/LivingVillage.Desktop')
| -rw-r--r-- | src/LivingVillage.Desktop/M6Presentation.fs | 69 |
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 |
