summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 21:54:01 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 21:54:01 +0800
commitaa527db03d7359535a78bc6b759f21aa354f6396 (patch)
tree288f6ea95c7786a5cb52bdeb202f5fba8a754c5d
parent87a00a036219f0f1b9c3eaccd7fcd9d782fe6011 (diff)
downloadliving-village-aa527db03d7359535a78bc6b759f21aa354f6396.tar.gz
docs: 记录预乘 alpha 机理与夜间光晕/炊烟接线
- Desktop 基线更新为 79/79 - 新增预乘 alpha 机理、lanternGlowLayers、per-tile 水面、炊烟后置遍、角色站位说明 - 补白块校验命令与 P14 证据位置
-rw-r--r--docs/维护说明.md37
1 files changed, 34 insertions, 3 deletions
diff --git a/docs/维护说明.md b/docs/维护说明.md
index 6978a9a..4635b6d 100644
--- a/docs/维护说明.md
+++ b/docs/维护说明.md
@@ -1,6 +1,6 @@
# 维护说明
-结论先行:主分支保持 0 警告 0 错误 + Desktop 75/75、Kernel 79/79 全绿;性能基线
+结论先行:主分支保持 0 警告 0 错误 + Desktop 79/79、Kernel 79/79 全绿;性能基线
final_digest 固定 953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C。
本文记录模块职责、不变量与逐字可复制的验收命令。
@@ -45,10 +45,33 @@ final_digest 固定 953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F
`avatarMoving`(比较前后位置);NPC 用 `npcSpriteSpecAtTarget`(目标向量 ≥0.5px 视为移动)。
- 世界图集 `Assets/jiangnan-world.png`:800×32,25 格。水面 3 帧在 slot 1/13/21,
炊烟 3 帧在 slot 22/23/24。
- - `waterFrameTick = (tick / 32) % 3` → Water / WaterSpriteB / WaterSpriteC;
- - `smokeFrameTick = (tick / 40) % 3` → SmokeSpriteA/B/C,画在民居屋顶上方 `offsetTile pos -2 -3`。
+ - `waterFrameTickAt tick x y = (tick / 32 + x + 2y) % 3` → Water / WaterSpriteB / WaterSpriteC。
+ 相位按**世界 tile 坐标**偏移,所以同一帧里相邻河面砖显示不同相位;`waterFrameTick` 仍保留为
+ 全局相位(旧断言)。
+ - `smokeFrameTick = (tick / 40) % 3` → SmokeSpriteA/B/C。炊烟在 `drawWorld` 的**末尾单独一遍**
+ 绘制,位置 `offsetTile (house door) 0 -3`(屋脊正上方),保证画在屋瓦、竹子等所有 prop 之上。
- 以上帧选择只用 `tick` 与「是否移动」,无墙钟/`System.Random`;同 tick 必得同帧。
+## 预乘 alpha 与夜间光晕/炊烟接线(P14)
+
+- **机理**:MonoGame `SpriteBatch` 默认 `BlendState.AlphaBlend` 按**预乘**语义合成
+ (source blend = `One`,destination = `InverseSourceAlpha`)。因此直传未预乘的
+ `Color(r,g,b,a)` 会把 rgb 按原值直接加进帧缓冲:多层暖色叠加在核心区饱和成
+ **不透明纯白方块**(P13 夜晚灯笼的 75×75 白块即由此而来;`Color(255,0,0,60)` 实测渲染为
+ `(255,63,68)`)。光晕、色调等任何 alpha<255 的前景绘制都必须**按自身 alpha 预乘 rgb**。
+- **正确写法**:`VillageArt.premultiply r g b a`(每通道 `round(c * a / 255)`,四舍五入;
+ alpha=255 为恒等)。预乘后单通道 ≤ alpha,alpha<250 时不可能出现纯白。
+- **夜间光晕**:`VillageArt.lanternGlowLayers cx cy radius rings r g b peakAlpha` 用同心圆
+ pixel run 近似径向衰减,每层色值预乘、核心多层叠加≈`peakAlpha`、边缘单层最暗;
+ `Game.fs` 只在 `renderPlan.Props` 的 `RiverLantern` 实体处绘制,无灯笼处不产生光斑。
+- **炊烟**:见上「动画语义」——烟雾是独立的后置绘制遍,与灯笼光晕同理。
+- **角色站位**:`characterDestination` 把精灵下移 `characterFeetOffset = tilePixels/2`(16px),
+ 使脚底贴合地砖底线;`drawGroundShadow` 用 `ellipseRuns` 画预乘半透明椭圆接触阴影。
+- **回归**:Desktop 单测 `PremultiplyScalesEachChannelByItsOwnAlpha`、
+ `LanternGlowLayersArePremultipliedAndRadial`、`WaterRipplePhaseIsDistributedPerTile`、
+ `CharacterFeetSitOnTheTileGroundLine`;图像级 `scripts/check-no-white-blocks.py`
+ 对夜晚录帧断言不存在 ≥64×64、每通道 ≥250、alpha ≥250 的方块(P13 旧帧 42/69 命中,修复后 0 命中)。
+
## 性能基线命令
```bash
@@ -97,6 +120,11 @@ timeout 45 xvfb-run -a -s "-screen 0 1280x720x24" env \
- `LV_RECORD_DIR` 默认 `/tmp/lv-p5-prompt`,`LV_RECORD_EVERY` 默认 25(每 N 次绘制存一帧)。
跑完 `sample result=ok`;连续移动帧(如 day-run 的 0136..0146)即走路逐帧采样。
+- 夜晚白块图像级回归(0 命中 = 通过;对 P13 旧帧应报 42/69 命中):
+
+```bash
+python3 scripts/check-no-white-blocks.py evidence/night-run
+```
## 取证快照位置(近期)
@@ -107,4 +135,7 @@ timeout 45 xvfb-run -a -s "-screen 0 1280x720x24" env \
- `/tmp/opencode/lv-p13/evidence/`(P13:`walk-idle-frames.png`、`water-3phase.png`、
`smoke-3phase.png`、`water-2frame.png`、`smoke-2frame.png`、`day-panorama.png`、
`night-lantern.png`、`day-night-compare.png`、`walk-real-run.png`、`day-run/`、`night-run/`)
+- `/tmp/opencode/lv-p13-evidence/`(P14 修复后重录:`day-panorama.png`、`night-lantern.png`、
+ `day-night-compare.png`、`chimney-closeup.png`、`night-lantern-closeup.png`、
+ `white-block-check.txt`、`day-run/`、`night-run/`;同步一份到 `evidence/`)
- `artifacts/perf-independent/<TS>/`(digest 3 联测回读)