从像素到动态世界:完整源码技术拆解
《清明上河图》并非“整张古画套一个 Three.js 特效”。真正的工程是:独立图层资产、程序化三维、二维约束蒙皮、统一叙事时钟,以及严格的遮挡与资源调度。
01 / 研究范围与可信度
对象为 xianxie6/qingming-riverside,来自用户提供的微信文章。固定版本 c575d242194fddafe33ec70dd01b860a75f09686,不是泛指所有同名项目。
| 范围 | 已完成 | 不能升级成的结论 |
|---|---|---|
| 文件级 | 250份源码、测试、文档和配置,逐文件长度与SHA-256;83个src模块盘点 | 不是逐个资产许可审计;不包括上游画片、音频和vendor实现 |
| 核心路径 | 入口、时钟、相机、GPU粒子、几何、水层、人物、遮挡、天气、加载、发布的深读 | 不是穷尽每个分支,也不是完整安全审计 |
| 非默认模块 | 探索、店铺、节庆、扩展街区职责和关键实现扫描;巨大坐标表只查结构 | 不宣称每个手工坐标都正确 |
| 测试 | 45个上游测试文件的内容和局限审查 | 本次未执行上游测试,不把桩测试当真实GPU或FPS |
| 重建 | 独立敦煌WebGL粒子、相机、尘粒、丝带、反射及交互,原图图库保留 | 不是完整三维洞窟,也未完成逐人物骨骼动画 |
逐文件证据清单 · 视觉深审全文 · 系统深审全文与70模块表 · 本地验收记录(未随公开源码分发)
02 / 真正的系统架构
从入口到画面的调用链
index.html → crossing.js → crossing-three.js → motion / world / details / courtyard / hero / water → renderer。穿越结束通过 AtlasTour.enterBridge() 进入街市;不是切回同一张放大图片。
街市则是 atlas.js → dynamic-loader.js → scene.js → world / movement / inhabitants / districts / weather / seasons / water → Canvas2D。人物内部还有原生WebGL PeopleMesh,因此“最终是2D”和“内部用了GPU”同时成立。
历史 crossing-tunnel.js 未被当前主链导入,也不在默认发布白名单。变量名 tunnel 在当前版本实际装载庭院,不能只看名字猜效果。
03 / 核心算法,逐层拆开
| 技术 | 实现机制 | 定位与工程含义 |
|---|---|---|
| 叙事时间轴 | target/current/playing/held分离;dt封顶;指数追踪,自动全程设计约50秒 | src/crossing-motion.js:16-25;刷新率不应改变叙事速度 |
| 程序几何 | 拱桥构件、甲板、栏杆实例化;船体截面拼面,篷顶管线 | src/crossing-world.js:36-150;有真实坐标、遮挡和体积,但远岸仍是贴图平面 |
| 粒子人物 | 透明像素采色;桌面80k/移动24k低面球实例,每球20顶点/36三角形;浅深度扰动 | src/crossing-hero.js:3-79;不是人体重建,不是标准骨骼数字人 |
| 鼠标扰动 | 世界坐标距离核衰减,局部排斥;聚拢、旋动和burst共享着色器 | 同上;不是CSS transform,也不是物理粒子碰撞求解 |
| 角色走路 | 路程驱动步态,两骨IK、脚底接触、躯干/袍/腿分区变形 | src/movement.js:48-200;得先有角色alpha层和手脚锚点 |
| 人物GPU | 静态纹理Map复用,动态顶点bufferSubData;另有Canvas仿射网格回退 | src/inhabitants.js:6-69,350-521;二维绘画网格而非三维骨架 |
| 反射与合成 | 低分辨率镜像相机、斜裁剪;反射3帧/阴影6帧更新;庭院另target混合 | src/crossing-three.js:45-126;多pass有成本,不能只统计一次render |
| 水流 | 相位平流噪声混合、正弦波、差分法线;瀑布反解下落时间 | src/water-three.js:7-122;艺术近似,非CFD |
| 内容行为 | 六组编排故事、渡船阶段机、避让clock、牵绳力反馈 | src/street-life.js、src/world.js、src/bridge-event.js;不是NPC各自调用LLM |
| 昼夜四季 | 雨/湿/风分通道,逐人开伞;灯窗配准;季节切换背景和衣服资源 | src/weather.js、src/night.js、src/seasons.js;不是整图滤镜 |
关键公式是通用图形/动画方法,而非受限代码的转抄:平滑阶跃为 t²(3−2t);指数追踪权重为 1−exp(−k·dt);排斥按距离平方指数衰减;二维腿部IK由两段长度和脚目标共同约束。源码内的参数是绘画世界尺度,不能冒称文物测量值。
真正费工的,是图层与遮挡
主画面顺序:建筑缓存 → 湿地/水面 → 夜色 → 透明前景 → 船与居民 → 家具/行人遮挡 → 瀑布 → 渡船客与舷边 → 环境船 → 雨棚/风物/蒸汽/雨雪 → 最终灯饰。
只对整张壁画做UV扭动,无法表现人物在桌后、船客在舷边内、手握住桨。要复现这种局部真实动作,需要逐母题拆层、接触点、蒙皮权重、补底板和遮挡;目前敦煌十张整幅资源尚不具备这套人物层资产。报告不将整幅粒子运动冒称“人物已经舞动”。
04 / 发现的问题与证据门槛
高可信静态缺陷,运行影响未测: src/crossing-water.js:13 插入的共享GLSL引用 noise()、bankAt(),但该过场片元着色器中未见相应定义;29行 shadeRiver 使用3个实参,共享定义要求4个。应针对真实GPU编译/链接验证,而不是只跑JS测试。没有运行上游,不能据此断言作者线上必定黑屏,也不能反过来宣称上游效果已测正常。
其他边界:仓库存在不等于发布启用;装饰数字不等于真实遥测;访问计数不是去重UV;受限视差不等于自由漫游;维护45个测试文件不等于45项实机验证。文章中的人数、FPS、加载秒数、传播量与“OpenAI点赞”未作独立证实。
05 / 加载、性能与生命周期
资源并发请求但经典脚本保序执行;等待ready/error屏障。可见街区优先、promise去重、超时失败可重试;建筑缓存带视区边界余量,未备好保留最后完整帧。四季切换带revision防止迟到请求覆盖当前选择。
上游预算为80万/140万渲染像素、DPR上限1/1.25、反射256/512、阴影512/1024;至少2秒30帧采样且平均帧耗超过26ms才降级。这是策略,不保证所有手机60FPS。销毁取消RAF、解绑监听、释放GPU;异步请求取消仍有局部改进空间。
06 / 发布与后端
npm run build 的CLI缺省采用 crossing 精简profile,build:all才全量。默认移除探索、孙羊店、中秋/烟花和扩展街区;不能将仓库全部功能算进线上首屏。源码含Three vendor,但package.json不直接给出其版本,不能硬猜。
Worker主要提供可选 /api/visits POST,以D1 UPSERT记请求数,其他交给静态ASSETS。没有以实时大模型驱动主动画的证据。GitHub工作流跑CI、构建、dry-run后发布;本次未执行上游部署脚本,避免向作者仓库写入。
07 / 敦煌 V0.4「空远」:自然度修正与全屏观画
用户实测认为V0.3动作不自然。根因在于面部核心也处于顶点权重梯度中,五官相对位置会改变;镜头持续余弦推拉、重复画片视差与过多装饰又加重了整张纸漂移的感觉。
V0.4的壁画几何保持平面不变。片元根据人工椭圆与枢轴选择唯一角色,核心采用同一个逆向刚体采样,羽化放在外围;同源曲线形成休息—缓起—停留—缓回,关节角度小于0.018弧度。衣带保留固定端,末梢才有更明显的微风。默认相机固定、整幅适配,导览点/时间轴/特写保留为主动观看;移除镜像、浮石与重复画片。它仍不是拆层补底或完整骨骼,外围接缝与大幅运动的限制没有凭空消失。
原创禅意轻音乐不使用佛曲录音、经文或采样包。声音由振荡器、稀疏音符与确定性双声道混响产生,音乐默认关闭。19项Node检查与真实GPU、连续帧、暂停、音频RMS、视口几何和扫码弹窗分别验证;物理手机、听感与用户视觉验收仍独立保留。版权及公开代码不分发壁画扫描的边界见COPYRIGHT.md。
本地历史运行截图不随公开源码再分发;本节是技术记录,不作为当前视觉验收。
V0.3 历史基线(已被用户拒绝自然度,不是当前验收)
默认进入反弹琵琶,中央舞者头颈、躯干与上举前臂分别按人工定位枢轴作小幅旋转,披帛和衣褶使用另一组柔边局部纹理/深度扰动。局部关节不受环境保护掩码禁动;脸部保护区禁止通用云水UV扰动,但不承诺网格权重内五官每个像素绝无形变。这是完整不透明画片上的局部网格演绎,不是精细alpha人物层、三维人体骨骼、独立步态或真实拨弦。
本地历史运行截图不随公开源码再分发;本节是技术记录,不作为当前视觉验收。
十幅共35个局部材质区、6幅8处局部关节;九色鹿颈首、菩萨/主尊微倾、维摩诘头颈与帷幔,各有定位和节拍。五台山与复杂三层/群像四幅保留静止人物,使用云水、山林层次和分段运镜。六类材质机制在十个profile中组合,不把十个类型名称冒充十套完全不同的渲染算法。矿彩52k/窄屏22k是可选模式,不再是默认唯一特效。
本地历史运行截图不随公开源码再分发;本节是技术记录,不作为当前视觉验收。
| 文件/层 | 独立机制 | 本轮证据与边界 |
|---|---|---|
| mural-scenes.mjs | 人工UV区域、枢轴、面手保护、导览停点和节拍 | 十图配置自检;不是自动分割或考古测绘 |
| mural-effects.js | 密网格柔权局部旋转,衣带/云/水/圆光/帷幔/明暗六类Shader | 十图实际GPU链接通过;羽化网格仍可能牵动附近背景 |
| scene-motion.mjs | 分画镜头、横卷推进、原作全幅适配 | 固定相机特写确认局部变化;不支持画后自由漫游 |
| scene-stage.js | 窟门构件错峰装配、夜灯与主动合成音境 | 昼夜、主动音频context实测;非历史音乐复原 |
| experience.js | 同一时钟驱动、迟到请求隔离、暂停/原作停帧、切画资源释放 | 两次暂停截图逐像素相同;一轮十画纹理回3,非长时无泄漏证明 |
Node9项、原图/HTTP完整性、逐画GPU、固定镜头人物实录、暂停、音境、原作和390px布局分别验证;物理手机、减少动态/失败/弱网分支与腾讯443尚未完成。数据见本地验收记录(未随公开源码分发)、本地验收记录(未随公开源码分发)、逐图依据、完整功能差距。原图未改动,前两版备份保留。
观看必须使用本地HTTP入口。直接打开index.html的file协议无法加载ES模块;新增明确提示与双击启动脚本,文件协议分支的浏览器检查被自动化安全策略禁止,未绕过、未宣称实测。
07-A / V0.2 历史基线:通用矿彩点云
本地历史运行截图不随公开源码再分发;本节是技术记录,不作为当前视觉验收。
以下为已备份V0.2历史基线,不是当前V0.3的默认效果:首页启动本地Three.js 0.186.0 WebGL2;从真实壁画取色生成浅层点云,叠加尘粒、GPU丝带、几何、镜头和反射。十画共用效果未满足用户逐图/人物动态要求,故本轮以上节方案重做;原图库和旧证据保留。
本地历史运行截图不随公开源码再分发;本节是技术记录,不作为当前视觉验收。
| 参考能力 | 敦煌独立实现 | 当前边界 |
|---|---|---|
| 真实GPU动态 | 采色点云、深度散开、相机、尘粒、丝带、反射 | 点云为艺术浅深度,不是文物测绘 |
| 叙事时钟 | 自动往返进度、手动seek、暂停/继续/重播 | 不是完整九色鹿剧情交互游戏 |
| 多画面资源 | 十画切换重采样、原图档案保留 | 不是十幅逐人物拆层骨骼动画 |
| 质量与回退 | 像素/粒子预算、离屏与隐藏停帧、减少动态默认暂停 | 390px仅浏览器视口,不冒充真手机 |
| 来源与权利 | 10份原图哈希、文件级许可、MIT依赖核验 | 8件长边2K+,2件待升级;无官方合作 |
新实现入口:experience.js,纯函数:motion-core.mjs,设计合同:DYNAMIC_DESIGN.md,实测:本地验收记录(未随公开源码分发)。上游根LICENSE不允许任意复制/修改/分发,敦煌项目未复制其业务源码、美术和音频;MIT只覆盖独立取得的Three与Reflector。
08 / 首批资源与后续门槛

原图/网页衍生分离,不AI补绘、不超分、不冒称原色。完整文件来源、窟号、年代、尺寸、许可主张与哈希见 RESOURCES.md 和 manifest。公开可看与正式商用授权是不同命题。
发布状态:本地候选、公开源码、云端上传和公网HTTPS验收分别记录,任何一项都不代表其余项完成。若进一步要求人物独立行走、完整舞蹈或真实持琴接触,应逐件制作合法的alpha母题层、锚点、补底和遮挡,不以本轮局部演绎冒充。
附录 A / 视觉渲染源码深审全文
以下保留完整原创技术解读。与总报告合读,视觉分工文末的“其他模块待审”已由附录B补齐;上游运行未知依然保留。
清明上河动态视觉链路深审与敦煌独立重建验收
审查日期:2026-10-23。固定版本:xianxie6/qingming-riverside@c575d242194fddafe33ec70dd01b860a75f09686。
本报告为原创技术解读,不复制上游长段实现。证据来自本地冻结源码静态逐行审查,未安装、构建或执行上游。源码存在、行为设计、实际GPU编译、设备运行和用户视觉接受是不同证据等级。这里不能把前两者写作后三者。
源码永久链接前缀:https://github.com/xianxie6/qingming-riverside/blob/c575d242194fddafe33ec70dd01b860a75f09686/。下文相对路径和行号均可直接拼接为该版本的 #L起-L止。
一、结论与边界
实际系统是三个连续阶段:真实程序化三维虹桥/船只 → 2.5D粒子绘画人物庭院 → Canvas2D可行走长卷(局部GPU水层)。不是十张图的相册,也不是完整三维汴京或全视角数字人。
- 虹桥、船、岸体具备真实世界坐标、透视投影、实体几何、相机路径、局部动态反射。
- 远岸主要是分深度布置的贴图平面;庭院是图片背景;粒子人物是从透明位图采色的浅浮雕,不是完整角色网格。
- 过场交互主要改变一个0–1叙事进度及小范围视差,不是第一人称自由行走。
- 主街市使用Canvas2D显示上下文。Three.js水层是离屏GPU画布再合成到世界坐标;因此独立重建不能只换皮CSS卡片,也不能虚称全场景3D。
- 代码/素材未获得一般复制修改再分发许可。独立敦煌项目可以重建上述抽象技术能力,但不得搬运上游代码、美术或音频。文物画面必须沿用独立核验的真实壁画来源及逐文件许可。
二、entry → input → state → camera → render → GPU → dispose
2.1 进入、预热、并发准备
src/crossing.js:3-19:warmCrossing()在pointerenter、pointerdown、focus时插入modulepreload与四张图片preload;尊重Save-Data,不在预热阶段创建WebGL或粒子。
src/crossing.js:21-45:点击后以active guard防重入,新建AbortController与原生dialog,按钮状态禁用,随后播放可关闭的音乐。file:协议分支提供本地服务器提示,不当作正常运行入口。
src/crossing.js:79–80,104–116:并行启动街市加载、目标落点准备,同时动态import createCrossing()。这里的“过场播放”与“后续可玩场景准备”相互重叠,减少场景切换空白。
依赖图:
crossing.js → crossing-audio.js(window.CrossingAudio)
→ dynamic-loader.js → scene.js/全局行走与人物子系统
→ crossing-three.js
├─ crossing-motion.js / crossing-quality.js
├─ crossing-world.js → crossing-details.js
│ → window.ScrollFeatured / Inhabitants / ScrollMovement
├─ crossing-water.js → water-three.js共享GLSL / window.ScrollWater
├─ crossing-traces.js(Canvas2D投影标注)
└─ crossing-courtyard.js → crossing-hero.js
注意:src/crossing-tunnel.js虽然存在独立65°视角的环形隧道、线框和坐标精灵(1–51行),当前crossing-three.js没有导入它,当前发布白名单也不包含它。变量名tunnel在当前版本实际承载createCourtyard()返回值。不能把历史隧道效果写成当前有效链路。
2.2 输入不是散落动画,而是统一控制时间轴
- 滚轮先统一deltaMode,单次位移裁到±220,再除4800映射进度;Ctrl+wheel不接管,按钮/输入控件不劫持。
- 拖动以起始进度加纵向位移÷(视口高×1.7)求目标进度,使用pointer capture。
- 方向/Page键每次±0.055;空格切自动,Home归零,End请求末尾。
- range输入÷1000,手动上限0.95;会面和离场阶段锁定一般导航。
- 鼠标位置转为[-1,1]归一化坐标,之后平滑影响相机/粒子排斥;touch不伪装hover。
src/crossing-motion.js:16-25:createJourney()区分target/current/playing/held;seek暂停自动,auto以dt/50推进(全程设计约50秒),dt裁至0.08;current以指数追踪target,系数1-exp(-6dt),避免刷新率不同导致速度不同。
数学核心:平滑阶跃t²(3−2t),与指数衰减追踪,而不是CSS时间长度硬切换。装配progress按部件order错开,起点0.19×order,终点再加0.20(crossing-motion.js:2–4)。
2.3 状态机与后续场景交接
src/crossing-three.js:131-154:RAF dt裁0.06;页面hidden时不推进;庭院尚未准备时在0.68等候。未离场时target不超过0.95,progress≥0.948进入meeting并暂停;guide解除会面并继续到1,末端持续0.25秒后resolve。
src/crossing.js:47-65:会面界面是固定问答字典,并非LLM对话。Guide交给visual.guide,Alone直接enter。不得宣传为实时大模型人格或自主智能体。
src/crossing.js:81-99:enter等待后续场景prepared,提交阶段防重复关闭,调用AtlasTour.enterBridge(),成功后720ms淡出;失败显示重载和返回;prefers-reduced-motion直接进入可玩场景。
2.4 相机设计与三维边界
src/crossing-motion.js:5-14:初始横屏相机约[4.8,5.7,22],竖屏[4.5,7.8,46];过渡近点[0.5,2.15,10.2],末端[0,1.7,−8]。靠近窗口0.35–0.52、穿桥0.50–0.70;视线Z由0移至−20。船Z从7.8到−13,窗口0.42–0.72。
src/crossing-three.js:34–39,98–105:透视FOV44°、near0.12、far230,指数雾密度0.013,半球光/主光/补光。鼠标指数平滑1-exp(-3dt),横向最多约1.05、纵向0.5世界单位,在0.38–0.72渐隐视差。不是OrbitControls,不允许绕到平面后面。
三、程序化物体、二维角色变形、镜头跟随
3.1 虹桥真实构件而非把图片放大
src/crossing-world.js:36-99:倒角ExtrudeGeometry构件配合三批InstancedMesh;拱曲线是抛物线近似,高度由中心4.77左右衰减至两端。四道拱肋、45块桥面、两边栏杆逐件落位。每个部件记录目标位置/缩放/四元数/order/扰动,装配时以未到位比例叠加偏移,矩阵写入instance buffer。
src/crossing-details.js:4-9:部件到位后仍有弱呼吸(强度由未完成占比衰减),三轴不同频率的sin/cos和微roll避免生硬冻结。
src/crossing-world.js:68-81:边线利用instance matrix共享buffer生成线段批次,透明shader按距眼衰减;不是每根木头分别创建一套描边材质。markers取真实部件当前位置及arrive值(182行)。
src/crossing-traces.js:8-29:3D部件投影到Canvas2D叠层,裁去近远平面外/控件遮挡区,42×26间距抑制重叠,显示实际装配百分比。与庭院的装饰数字不同:crossing-courtyard.js:47–54数字是随time和索引生成的视觉标签,不能叫真实扫描精度/数据遥测。
3.2 船与船夫
src/crossing-world.js:117-150:船体截面沿Z采样、拼索引面;船篷以半圆截面和Tube骨架实现。具备真实空间体积,但后续材质大量换为从插画切取的墨色纹理(155–164),以统一艺术风格。
src/crossing-world.js:165–179,206–228:可用全局人物素材时,船夫由33列/49行基准采样并额外纳入握点行列构成二维可变形网格;帧上CPU更新顶点,人物面向相机。createRowingRig()先求手臂,再经rowingPoint()平滑蒙皮;桨沿上下握点方向延伸至指定水位,握柄Z凹入−0.026,避免桨穿出拳头。它不是标准骨骼glTF角色。
src/crossing-details.js:21-88:3.6秒划桨周期,手臂两段长度保持,利用movement.legJoints解肘关节;骨影响按距离四次衰减并连续肘混合,拳头刚体旋转;划水力度由周期0.08–0.52的半正弦决定,恢复段抬桨。
src/crossing-world.js:199-204:船随progress前进,同时独立以time轻微浮沉与摇摆;旗帜顶点以sin随时飘动。停止时间轴不必冻结所有环境微动,这是叙事进度与环境时钟分离的设计。
3.3 背景是舞台布景板
src/crossing-world.js:184-197:街景PlaneGeometry放在左右、近远不同深度,侧向不同旋转;片元以边缘smoothstep和颜色亮度阈值遮掉纸白。它提供有限镜头中的遮挡和视差,而不生成建筑背面、室内和自由行走空间。
四、GPU多pass、反射、纸感与庭院
src/crossing-three.js:14,28–38:renderer启用抗锯齿、高性能偏好,SRGB输出、ACES filmic、曝光1.10,PCF阴影且自动更新关闭。
src/crossing-three.js:45–70,107–126:
- 低分辨率半浮点反射target,镜像相机的Y映到
2×waterLevel−eyeY,隐藏水面防自反射,使用斜近裁剪排除水下几何,每3帧更新。 - 阴影每6帧更新;三维世界画入paintedFrame,桌面2样本、移动0样本。
- 庭院单独画到自己的target,在0.685–0.765区间混合。
- fullscreen quad最终合成:四邻域颜色梯度生成暗边,强度0.21;从全景图底部细条采纸纤维,增量约0.025;庭院亮部9点加权采样形成微弱泛光,系数0.12。不是完整后期composer、不是物理曝光/真实纸张仿真。
src/crossing-courtyard.js:5–31,35–71:FOV39°,真实背景位图cover裁切;粒子主角、3.8×3.8地面透明接触影、350尘点、圆柱线框笼和6个数字精灵。主角形成窗口0.735–0.925;burst在0–0.8秒增强,在1.5–5.5秒消退;会面2.2秒后笼和标签淡出。鼠标视差只让水平角变±0.035弧度左右,竖屏相机距离14.5,横屏约10.5–11。
五、粒子画像如何形成和交互
src/crossing-hero.js:3-18:球形颗粒保留36三角形,合并接缝顶点后20顶点,减顶点shader执行数量。默认桌面80000实例、移动24000;不能只看“8万粒子”就理解为8万点绘制,它们实际是实例化低面球体。
src/crossing-hero.js:22-72:加载透明人物图、Canvas一次性getImageData,拒绝alpha<245像素;随机采样并保持像素颜色。人物高度映射为4.7世界单位,浅深度为低幅sin/cos加极小随机扰动(主幅0.10,次幅0.035),维持绘画面貌。每2048次检查CPU切片,超过8ms让出事件循环。
src/crossing-hero.js:31–51,74–79:实例粒子有scatter、phase、radius三个自定义attribute;聚拢smoothstep按phase错开,松散比例为未到位与burst的较大者;drift与vortex提供聚拢旋动。鼠标用世界坐标排斥核exp(−distance²/1.15),最大扰动系数约0.32;cursor指数追踪(系数5),hover指数追踪(系数3.2)。鞠躬只影响高度1.6–3.8以上区域、脚底不动,2.4秒半正弦。
因此对敦煌改造要先决定“全幅壁画粒子化”还是“经人工合法分层的人物粒子化”。直接拿全不透明矩形古画走此采样,只会形成矩形粒子平面;不能冒称壁画人物自动抠图、深度重建或可绕行数字人。原壁画不得为视觉效果默默篡改成伪复原。
六、街市水层与过场水层的真实算法
6.1 共享近似河面
src/water-three.js:7-52:岸边流速7世界像素/秒,河心以smoothstep升至22;3层噪声权重0.52/0.31/0.17。两份7秒周期错半相位的平移噪声交叉混合以避免不同深度流速撕裂;长亮缝另用11秒周期。波高是5个不同方向正弦叠加,频率k=0.05+0.026i,相位速度按sqrt(58k),再叠加平流细波。属于艺术近似,不是Navier–Stokes或实测水体模拟。
src/water-three.js:67-84:把screen UV反算世界画卷坐标,在河岸下16像素淡入;0.7世界像素差分估法线,偏折原底图采样约dx×16、dy×3.4,镜面幂40;超过画卷底部按fadeY淡出。水层不是渲染完整3D河床,是对原画水区重采样及着色。
6.2 水磨瀑布
src/water-three.js:85-122:单独pass用自由落体时间反解tau=(sqrt(v0²+2gy)−v0)/g,g=185、初速35;以tau−time平移噪声使条纹向下加速。下部水幕变宽、边缘破碎、孔洞、飞溅及冲击泡沫/环/薄雾由噪声和距离场组合。参数是绘画像素尺度,不是米制真实物理数值。
6.3 传输与合成优化
src/water-three.js:131-199:透明、无抗锯齿、预乘alpha renderer;像素比不超过1.35。仅裁河岸以下的底图并多留20世界单位边距,纹理无mipmap、线性过滤,backdropKey改变才上传;scissor在片元shader前限制水面/瀑布区域,最后drawImage回Canvas2D世界坐标。上下文丢失置active=false、恢复清缓存,但此类没有公开dispose方法,独立重建应补齐生命周期。
src/crossing-water.js:59-77:过场河面是240×240三维平面,水位0.015,载入夏季河水纹理;最多12条环形水波事件的循环buffer,桨力越过0.16发一圈,船速0.1–8且间隔>0.32秒发尾流。波寿命3.8秒,三环半径每秒增0.34,透明度平方衰减;反射强度用视角近似Fresnel从0.025到0.08,而非完整物理材质。
6.4 关键静态缺陷:过场共享shader不自洽
高可信静态缺陷,运行影响未实测。src/crossing-water.js:13插入ThreeWaterRenderer.riverSurfaceShader,而该字符串的ripples/seams/heightAt引用noise()与bankAt();这些函数只定义在water-three.js的另一个完整fragmentShader里(59–64行),不包含在被导出的字符串内。过场自身fragmentShader未补定义。
另外crossing-water.js:29用3实参调用shadeRiver(base,spec,h),但共享定义在water-three.js:45需要第4形参streak。静态上应出现未声明函数/参数签名不匹配的GLSL编译错误。Three.js着色器错误未必作为可捕获JS异常传播,所以crossing.js的try/catch不能被当作这类错误的有效fallback证明。
独立重建必须使用自包含GLSL模块、明确所有函数/统一变量依赖,在真实GPU上下文调用shader/program编译状态并收集错误,且验证最终像素,不得仅靠JS语法检查或静态字符串测试放行。
七、性能预算、释放、构建裁剪
src/crossing-quality.js:2-21:宽度<600认为mobile,核数≤4视limited;像素预算分别80万/140万。ratio=min(DPR、移动1/桌面1.25、sqrt(预算/面积));反射256/512、阴影512/1024。慢帧统计至少2秒且30帧,平均帧耗>26ms才将ratio×0.82,忽略dt>0.15的后台暂停。不自动升回;这只是降级策略,不能保证任何设备60FPS。
src/crossing-three.js:15-26:keep登记GPU资源,dispose幂等、取消RAF、AbortController解绑输入、resolve等待者、dispose各资源、移除2D痕迹画布、释放renderer、forceContextLoss并删DOM。异步迟到资源在keep中立即dispose。缺点是资源请求/人物CPU采样本身未收到独立AbortSignal,取消不保证网络下载和长任务立刻停止。
src/crossing.js:66-78:关闭dialog同时abort、音乐dispose、视觉dispose、恢复焦点与入口状态。音乐有revision防止旧play Promise回调覆盖新状态(crossing-audio.js:20–40)。
scripts/crossing-release.mjs:3–25,33–60:白名单包含当前crossing-courtyard/hero/details/water,不含历史crossing-tunnel。构建删除explore、餐馆、中秋入口与节庆代码/音频引用,保留后续四季街市。部分PNG是兼容老页面URL而保留,不能把仓库文件总量当首屏下载量。
八、敦煌独立重建的最低能力合同
以下是建议实现与验收,不是上游已证实结果。满足它们后才可称“借鉴完整动态技术体系的独立敦煌项目”。
| 环节 | 独立敦煌实现 | 必须提供的验收证据 |
|---|---|---|
| 入场 | 原画—窟门/藻井/回廊程序几何展开,滚动/拖动/自动统一时间轴 | 0/25/50/75/100%状态截图、连续正常速度录屏 |
| 真实空间 | 透视相机、近远遮挡、至少一组有厚度可观察的程序化几何;原壁画作有来源的画面层 | 相机矩阵/位置变化,轮廓和遮挡跨帧改变,不是图像scale |
| 相机 | 横竖屏不同构图,限制在真实画面可支持视差范围 | 桌面/手机宽高比录屏,边缘无露底拉伸 |
| 粒子 | 从合法壁画采色,GPU实例/点绘制、渐进重聚、可打散、鼠标排斥 | 粒子实例/点数、draw-call统计、至少聚合与分散两种真实截图 |
| 文物保真 | 默认可返回不变形原作;衍生动画明确标识;不虚称复原 | 原画比较/来源、改动说明与窟号核验 |
| 时间/输入 | seek、暂停、继续、重播、键盘、拖动均对同一状态生效 | 反向seek/端点/连点/切tab压力交互实测 |
| 场景交接 | 资源未好等候,成功后进入真正可交互场景,不以打开图库代替 | 慢网、失败、重试及完成主链路录屏 |
| GPU特效 | 与敦煌主题相符的尘粒/壁画色层/浅浮雕光感;不必强搬河水 | shader compile/link状态、控制台零图形错误、实际可见像素 |
| 自适应 | DPR/像素数/粒子预算限制,持续慢帧后降级 | resize日志、降级前后ratio/FPS采样,设备型号与采样时长 |
| 无障碍 | 减少动态、静音、跳过、键盘、焦点恢复,静态fallback保留内容 | 系统reduce-motion与WebGL不可用真实分支 |
| 生命周期 | 每次进入/退出均解绑/释放,不遗留多个RAF/WebGL canvas/音频 | 10次开关后canvas数、活动loop/监听计数、GPU资源趋势 |
| 资源/许可 | 原图自包含、逐文件源/尺寸/hash/许可/署名与衍生改动 | 白名单、无失效请求,许可证清单与独立实现声明 |
| 发布 | 仅本地预览候选,通过后再按授权部署 | 构建测试与浏览器实测分别列出;不擅自发布 |
不能将“十张高清壁画已下载”“页面可打开”“测试断言通过”分别升级成“完整动态逆向”“真实GPU已正常”“用户已验收”。完整源码分析还需要主交互/人物/天气/寻物等其他链路的独立审查,本报告仅覆盖指定视觉子系统。
九、定位速查
- 入口与交接:
src/crossing.js:7,21,47,67,81,96,104。 - 渲染器、输入、draw、RAF、dispose:
src/crossing-three.js:12,17,74,76,98,131,133。 - 状态与相机:
src/crossing-motion.js:3,4,5,16。 - 程序几何与装配:
src/crossing-world.js:6,40,65,84,184,199,206。 - 划桨变形:
src/crossing-details.js:21,46,77,85。 - 粒子生成/更新:
src/crossing-hero.js:3,22,31,53,74。 - 庭院形成:
src/crossing-courtyard.js:5,35。 - 水材质:
src/crossing-water.js:5,9,59;共享shader与CanvasGPU适配:src/water-three.js:7,53,127,145,150,194。 - 预算:
src/crossing-quality.js:2,11;标注:src/crossing-traces.js:5,8;音频:src/crossing-audio.js:3,20,36。 - 发布:
scripts/crossing-release.mjs:3,33。
此前报告请求核对的七个相对路径均准确存在:src/scene.js、src/crossing-three.js、src/crossing-world.js、src/crossing-hero.js、src/movement.js、src/dynamic-loader.js、src/crossing-quality.js。
附录 B / 内容系统与逐模块职责全文
清明上河:街市、角色、内容系统与交付链源码审查
审查对象:xianxie6/qingming-riverside,固定提交 c575d242194fddafe33ec70dd01b860a75f09686。审查日期:2026-10-23。本文为原创技术归纳,不附上游长段源码,不执行上游程序。
1. 覆盖声明:不要把扫描说成运行
- 已枚举当前快照全部83个
src/*.js与45个tests/*.test.cjs,并查看入口、构建白名单、依赖声明、Worker与部署工作流。 - 本分工不深审
crossing-*.js、crossing.js、water-three.js,这些由主审负责。本文在调用链中仅确认其接口与接入点。 - 深读层:
index.html、atlas.js、dynamic-loader.js、world.js、movement.js、population.js、street-life.js、weather.js、night.js主要机制、scene.js入口/状态/合成/主循环/输入/加载、inhabitants.js着色/蒙皮/批次/合成主路径、featured-characters.js、scene-backdrop.js、districts.js加载与遮挡、seasons.js、sound.js、visits.js、构建/发布脚本、代表测试正文。 - 模块职责扫描层:扩展街区、孙羊正店、古城探索、烟花/节庆等非默认发布模块,检查声明、导入导出、部分关键实现与对应测试;并非逐函数逐分支穷尽审计。数据模块的巨大坐标/人物表未逐点人工核验。
- 没有
npm ci、没有执行npm test/构建/上游脚本、没有在本分工启动上游网页、没有GPU帧率/音频主观/实机内存测量。因此以下测试仅描述“仓库编写了什么”,不代表本次全部通过。下载目录无assets/vendor原图或第三方源码,无法核验逐像素资产、Three.js真实版本及GLSL实际编译。
2. 结论与正确技术定性
项目不是“所有建筑与人物都做成完整3D模型”。默认街市可游玩段使用 Canvas2D 做最终显示,内部复用一个原生 WebGL 人物网格面与一个 Three.js 水层;穿越段另外创建真实Three.js场景。视觉成功主要来自手工校准的独立绘画层、稳定脚底/手部锚点、时间驱动的局部动作、精确遮挡与镜头调度,而不是给整张图一个CSS悬浮动画。
证据:scene.js:9-28 初始化2D显示与角色画布;inhabitants.js:6-22 创建独立WebGL PeopleMesh;scene.js:856-945 合成多层;scene.js:1021-1065 每帧更新。技术上应称“混合2D/2.5D街市 + 独立3D穿越”,不能一概叫全3D世界。
3. 从入口到像素的真实链路
index.html
├─ atlas.js:全景图 + 热点 + hash 路由 + 隔离非活动页面
├─ dynamic-loader.js:19个经典脚本并发请求、按插入顺序执行
│ └─ scene.js(type=module)
│ ├─ world / movement / population / street-life:世界与行为
│ ├─ Inhabitants + Featured + Workshop:人物切片、变形、动作
│ ├─ Districts + BackdropCache:建筑图层与可见区域资源
│ ├─ Nightfall / RainEvent / Seasons:环境状态
│ ├─ ThreeWaterRenderer:水面特殊层
│ └─ requestAnimationFrame → update → render → 2D最终画面
└─ crossing.js:3D穿越对话与镜头;结束后调用 AtlasTour.enterBridge()
3.1 全景与街市不是同一张图简单放大
atlas.js:3-10 全景坐标2172×724,4个热点映射不同 world 位置(-1550/650/1560/3360)。launchScene(88-116)先 loadQingmingScene,再等待选中街区资源 prepareQingmingEntry,派发 atlas-enter 后才隐藏全景。scene.js:1209-1228 同时搬移玩家和镜头,并清理旧乘船状态。isolate(atlas:83)给非当前页面元素设 inert,避免隐藏控件仍参与交互。
3.2 启动与可见资源优先
dynamic-loader.js:3-9 列19个依赖,add(24-29)设置 async=false,Promise.all(39)使请求同时发出、脚本仍按插入顺序执行。准备阶段通过 image preload 与 modulepreload 预热(13-21)。街市的“脚本加载成功”和“素材可用”分开:等待 atlas-ready / atlas-error,30秒超时,并防止失败ES模块被重复插入后永久等待(40-54)。页面load后低优先级预载受 file: 与 Save-Data限制(57-65)。
districts.js:8-32 根据视区只加载西/东必要画片,使用pending Map去重、20秒超时,可重试;选中场景画完后再低优先加载其余地区(scene:1229-1235)。render 遇未加载建筑保留最后完整帧,不在空背景上画人物(scene:863-873)。
4. 状态、时间与画面合成
4.1 三套状态不能混淆
- 页面/镜头状态
state:尺寸、DPR、缩放、相机当前位置/目标、是否自动行走、是否运行、模拟时间(scene:159-162)。 - 玩家状态
player:x、速度、目标、朝向、步态相位、跟随、待执行动作/已执行动作(同上)。 - 独立业务状态:ferry、Crossing、Nightfall、RainEvent、seasonController、StreetLife.Director(scene:74-113、178)。
frame 将 dt 限制至0.05秒(1022),背景tab/全景/节庆弹窗/加载范围时跳过街市模拟(1023)。uiTime可用于短时交互水纹,time只在running时增长(1034);人物、桥故事与雨事件收到运行开关对应dt。镜头以指数响应追目标,不再依赖某个固定刷新率(1051);接近物理像素百分之一后直接归位,避免静止时持续作废背景缓存(1052-1054)。
4.2 画面顺序本身就是核心算法
scene.js:856-945 真实渲染顺序:建筑缓存 → 湿地 → GPU水面或2D水纹 → 建筑夜光/天空 → 动态透明前景 → 过桥船 → 居民/桌椅遮挡/路人/水车/点击水纹 → 瀑布局部特效 → 渡船及乘客舷边遮挡 → 按水线排列的环境船 → 雨棚/风动小物/蒸汽/雨雪 → 前景夜色source-atop → 最终灯饰。
单纯将整张壁画UV扭动,不能复现这里的“人物在桌后、行人在桌前、船客在舷边后、水车遮路人”的效果。每个可动目标必须有独立贴图/遮罩/锚点或可信的局部图层重建。
BackdropCache.get(scene-backdrop:6-18)按视区宽高、密度和district revision缓存世界空间画片,并在两侧保留不超过160单位余量。镜头小幅移动仍采同一个缓存,不每帧重画整街;季节切换显式invalidate(scene:103)。
5. NPC与人物:不是实时AI代理
5.1 位置与行为是作者数据和确定性状态机
population.js:7 定义世界范围[-2172,4344],跨度6516、高724。居民数据32-65、步行路线67-72、职业映射74-86。成年人统一61–66单位,孩童与坐姿另设高度带(8-22)。scene.js:791 统计是居民+40个walker+5个legacy行人+1主角;不要将其他探索模块的320人直接算到该街市。
street-life.js:10-16 六个重复编排事件:茶铺、菜市、陶坊、布铺、儿童、货运。storyAt(33-87)按time取阶段、actor pose和prop owner;journey/itinerary(18-31)使用平滑区间保持动线连续。搬货被玩家上船打断时,挑夫从真实位置回码头而不是瞬移(127-142)。Director.advance(148-155)对每个walker维护独立clock:迎面避让/桥阻塞时速度率衰减为0,恢复后继续同一相位。
这是“编排式行为系统”,不是行为树+大模型对话,更非每个NPC独立推理。对本分工审查入口、前端主路径与Worker的限定搜索,没有发现为这些行为发起LLM请求;此结论不覆盖仓库外服务。
5.2 真正的局部动态来自约束网格
movement.stepWalk(29-44)将目标速度指数平滑,并精确积分,避免30/60/120Hz走出不同距离;短按目的地以press位置记账(12-27),非keyup临时补步。
gaitAt(48-60)用已走距离而非纯墙钟驱动步态;支撑期脚底落地、摆动期抬脚,至少一足支撑;legJoints(63-74)是二维两骨IK,坡地够不着时大腿小腿同步伸长。deform(162-200)按蒙皮区域施加躯干、点头、手部与脚部影响;长袍单独处理,不把下摆拆成两条裤腿。
inhabitants.prepareFrames(107-130)从图集裁人物,基于边缘flood-fill去纸底;不能一刀去白,否则白袖、瓷器等也会透明。paperSteps(153-177)仅连通外部纸色,边缘再反白污染。footContacts在alpha>96的脚底测接触点(movement:151-160)。衣服按上/下衣掩膜着色,头脸等受保护(inhabitants:192-214)。
sprite(350-469)把人物分成躯干/袍/独立腿面片,调用movement.deform产生网格顶点。动态绘制两条路:WebGL PeopleMesh把顶点与UV上传为三角形(6-69),或Canvas逐单元仿射采样(487-521)。走路人物每帧更新;普通居民约12Hz缓存动作画片,近看陶工更高频(388-403)。WebGL纹理Map至多128项,静态纹理只首次上传;顶点buffer复用bufferSubData(47-68)。这不是完整3D骨骼角色,但比整张sprite平移复杂得多。
Inhabitants.draw(291-349)按可见范围裁剪,居民 → 家具 → 路人排序,批次绘图保留独立合成区域;点击命中可添加4秒关注,不篡改原故事动线(290-295、299-318)。Featured.Characters(21-59)同样去底并测接触点,画师/船夫的抓握点从原图归一化换算,船桨由双手决定(15-19)。
6. 船、桥与交互因果
world.js:23-41 以手描桥面和台基路径求streetY,使用单调Hermite式切线限制避免折点。createFerry/stepFerry(60-96)明确等待→靠近→停泊→离开→返回;乘客额外boarding→sailing→disembarking→moored,航程48秒、上下船各3.4秒。passengerPose(116-124)沿连接岸与甲板的多段路径,不瞬间贴到船上。环境船同速等距循环避免互相追尾(49-55)。
bridge-event.js:9-38 虹桥剧情是idle→approach→call→rig→guide→clear→record→complete。牵引输入先以tension低通过滤,最佳力约0.56;过力>0.85增加overpull且减速0.006,船工仍保有最低推进、不会卡死。参与结果由累计work决定(16、24-35)。vessel(41-55)用缩放、轴向和位置曲线表现远近,不是刚体流体模拟。residents(57-64)将桥上目光/拉绳动作覆盖到已有居民。
UI在 scene.js:1067-1206 将拖拽、双指缩放、双击、滚轮、方向键、E、Space、Esc、牵绳滑块、声音与全屏映射到同一状态,失焦/visibilitychange清除held输入(1180-1182),避免回到页面还在持续走。可访问名称/slider值/状态播报均在HTML和DOM更新中存在,但不是本次无障碍实测合格证明。
7. 环境、四季与声音
- 雨事件:
weather.js:9-3588秒确定性阶段;rain/wet/gloom/wind分别插值。伞沿每人稳定hash分批展开(39-50);室内、阁楼、亭内不撑伞(44-45)。雨线、积水涟漪、湿地、雨棚分别绘制(78-117),不是只叠一层半透明蓝幕。 - 夜景:
Nightfall(night:9-16)正常12秒转换,reduced-motion约0.35秒。窗/灯位置手描到不同街区坐标(18-45),逐窗点亮(53),人群按稳定身份与角色分批淡出(61-75)。夜色对建筑与透明前景分开合成,避免灯光压过前景人物(scene:895-942)。 - 四季:
seasons.Controller.select(14-31)使用revision token,只应用最新完整请求,失败保留旧季且从promise cache移除失败项。背景三片与衣服/主角图集共同切换(scene:86-105),非仅CSS hue-rotate。冬季改snow,不再雨水/湿地(seasons:47-49),季节空气粒子位置取同一scene time(86-105)。 - 声音:
InkSound.create(sound:14-38)混录音循环与WebAudio合成雨底/瓷盏/敲击/划水/绳索。需用户操作后resume(5-12),不能默认声响自动播放。昼夜曲交叉淡入,旧曲延迟暂停(48-66),雨越大录音背景越低(119-125),按视区过滤事件与声像(93-115)。音源授权另有缺口,不应搬用。
8. 默认交付物与仓库实验场必须分开
scripts/build-static.mjs导出的API默认profile='all'(8),但CLI缺省profile='crossing'(50-52);npm run build调用CLI,所以正常构建是crossing精简版,npm run build:all才全量。不能只看导出函数缺省值误判线上发布。
crossing-release.mjs:3-25白名单包括穿越+街市+四季+水面和对应资源;crossingSource(33-60)变换入口移除explore链接、孙羊店入口、中秋dialog及脚本/样式,scene的festival import替换为空钩子,店家图加载删除,中秋音频地址改为日间。源仓库仍保留这些模块,并不表示默认dist包含它们。
index.html:18中秋data开关本身false;festival-street.js:3遇false直接return;烟花脚本未被入口引用、index:114明确停用。古城探索另有自己的explore.html、CSS/DOM人口和收集系统;扩展街区另有extension-preview入口生成脚本,默认构建没收这些模块。
构建先验证白名单路径与存在性、再只允许替换根下dist(build-static:17-40)。check-public-assets(36-55)递归检查相对URL和排除private/build-only文件;其正则/模板检查不能证明运行时条件路径100%可达或视觉正确。Three.js vendor只经esbuild minify,业务JS仍独立文件非一个框架应用bundle(29-35)。
package.json仅声明devDependencies esbuild 0.28.1、wrangler ^4.134.0与Node>=22;Three.js以vendor模块提供,不能据package.json硬猜其版本。没有React/Vue/数据库支撑人物世界的证据。
GitHub工作流 .github/workflows/verify-and-deploy.yml:13-44 是npm ci→npm test→npm run build→Wrangler dry-run→上传dist→Pages部署,只有master非PR事件触发部署。scripts/deploy-pages.mjs:13-18另设仓库远端/精简profile校验,通过临时gh-pages工作区把dist提交并workflow dispatch(65-70)。这是上游作者部署脚本,不应在本项目直接运行,它会推上游仓库。
9. 后端并不驱动动画
worker.js:4-20唯一业务路由 /api/visits 接受POST,D1 UPSERT返回累积count,其他请求直接ASSETS.fetch。CORS只对作者GitHub Pages origin返回头,失败503、不缓存。visits.js:6-14在本机直接不统计,线上按host选择Worker endpoint、3秒abort;失败只影响计数,不影响人物。此counter为请求次数,并非经过反作弊或独立访客去重的UV。
10. 测试证据如何正确解读
45个测试文件存在,但本次未执行。代表性映射:
| 测试文件 | 编写的检查 | 证据类型与局限 |
|---|---|---|
| world / walking / movement | 渡船上下船、刷新率等距、脚底连续与两骨约束 | 纯函数数值测试;可查算法边界,不等于真实画片无穿帮 |
| street-life / bridge-event / weather / night | 阶段有序、pause不推进、避让连续、过力可完成、伞和夜间人群 | 确定性模拟;不证明主循环所有接线和输入都正确 |
| loading / districts-loading | 并发启动、promise去重、错误重试、只拉可见街区 | VM+假的DOM/Image/event;不是真网速/浏览器请求瀑布 |
| rendering | 衣服/腿网格/缓存/alpha去底、GPU atlas复用 | 代理Canvas/WebGL、记录调用;GLSL并非由真实GPU编译,不能据此宣称实际纹理上传耗时已测 |
| performance | 分数DPR时画布不重复resize、孙羊guest复用backing store | 计数桩检查(tests/performance:6-41);不是FPS benchmark |
| scene-backdrop / water-upload | 缓存复用、source变动才needsUpdate、局部水区上传 | 纹理对象桩与调用次数,不是真显存或GPU时序 |
| entry / bridge-railing / water | 抽取源码片段执行或正则断言接线存在 | 结构回归对重构格式敏感;不等于真实鼠标/键盘路径端到端通过 |
| publishing | 临时fixtures检查资源路径、private不进dist、crossing裁剪 | 确有构建函数调用,但大量资产用'asset fixture'替代(publishing:55-58),不证明真实图音频可解码 |
| explore / collection / city-fusion / extension-* | 人口/遮挡/收集幂等、花店状态、步态/图集锚点 | 覆盖非默认模块;即使通过也不能把它当默认线上已启用 |
本分工对45个tests做了名称/正文模式检索,在已查测试中没有发现Playwright/Puppeteer/Chromium真浏览器驱动;不据此断言仓库外不存在手工验证。docs性能记录是作者记录,既非本次测量,也不能替代当下同设备线上数据。
11. 每个非穿越src模块职责简表
说明:D=主路径深读;S=正文/导入导出/代表调用扫描;G=生成数据/坐标结构扫描未逐点核验。water-three与12个crossing相关模块交由另份报告,本表列其余70个模块。
| 文件(均在src/) | 职责 | 审查层/默认crossing包 |
|---|---|---|
| atlas.js | 全景热点、hash、街市入口与可访问隔离 | D/是 |
| atlas-atmosphere.js | 北京时间、月相/天气相关首页氛围 | S/否 |
| atlas-festival.js | 配准于全景的节庆天空装饰 | S/否 |
| atlas-firework-shapes.js | 生成灯笼等烟花点阵 | G/否 |
| atlas-fireworks.js | 烟花粒子、尾迹、表演与首页UI | S/否 |
| boat-song.js | 与歌女动作同步的拨弦/元音声学合成 | S/否 |
| bridge-art.js | 虹桥船只投影、桅杆/绳索/桥前景 | S/是 |
| bridge-event.js | 过桥阶段机、拉力、进度和结果 | D/是 |
| bridge-railing.js | 桥栏遮挡路径和原画恢复 | S/是 |
| city-fusion-data.js | 城市人物配准/尺寸修订数据 | G/否 |
| city-fusion.js | 修订融合、景深排序、遮挡与家具 | S/否 |
| city-point-fixes.js | 城市人物个别手工位置修补 | G/否 |
| collection-data.js | 25目标、分组、碰撞/遮挡与存档清洗 | S/否 |
| collection.js | 收集UI、alpha命中、提示、进度持久化 | S/否 |
| districts.js | 西/中/东图加载、拼接、家具/建筑遮挡、水车 | D/是 |
| dynamic-loader.js | 并发有序脚本、预热与ready/error屏障 | D/是 |
| explore-atmosphere.js | 城市探索的世界坐标环境DOM动效 | S/否 |
| explore-data.js | 旧探索目标/彩蛋/交互内容表 | G/否 |
| explore-geometry.js | 矩形/多边形命中、最小面积目标、存档清洗 | S/否 |
| explore-people.js | V3人口DOM图集、LOD、镜头和收集入口 | S/否 |
| explore-population.js | 地区/职业定义与320绘制成员组装 | S/否 |
| explore.js | 旧12目标探索、拖拽缩放、提示/存档/音色 | S/否 |
| extension-flower-customers.js | 花店顾客进出门和店员服务姿态 | S/否 |
| extension-flower-shop.js | 买花/插瓶/旋转/近看dialog | S/否 |
| extension-flower-state.js | 试用币、库存、花瓶槽位、存档净化 | S/否 |
| extension-flower-street.js | 花街局部角色精修与独立走行路线 | S/否 |
| extension-limited-animation.js | 逐绘关键帧图集、脚底/头部校准与距离驱动walk | S/否 |
| extension-motion.js | 按职业动作周期、角色动作计划与街声 | S/否 |
| extension-people-data.js | 扩展街区人物/道路/atlas元数据 | G/否 |
| extension-people-layout.js | 显示比例、花店顾客角色拆分和位置修订 | S/否 |
| extension-people.js | 裁alpha图集、动作计划、puppet/limited渲染选择 | S/否 |
| extension-preview.js | 七画片长街、仓场/客栈交互、河船、性能调度 | S/否 |
| extension-puppet.js | 二维变形三角网格帧缓存,96MiB预算 | S/否 |
| extension-walkers.js | 连续路段、启停行走计划、步长与距离相位 | S/否 |
| extension-workshop-state.js | 起吊/卸包/搬运/回位、门帘、顾客/店员阶段机 | S/否 |
| extension-workshop.js | 道具资源、花肆门帘遮挡、仓场actor合成 | S/否 |
| featured-characters.js | 画师/船夫去底、双手锚点、脚底及boat尺度 | D/是 |
| festival-lighting.js | 节庆背景、月亮和灯光层 | S/否 |
| festival-street.js | 中秋独立街景及演员控制(入口开关false) | S/否 |
| firework-audio.js | 烟花录音buffer、背景loop与静音生命周期 | S/否 |
| inhabitants.js | 人群裁图、着色、GPU/Canvas蒙皮、批绘、命中 | D/是 |
| midautumn.js | 东街节庆门/灯笼和双pass灯光钩子 | S/否(发布替换为空钩子) |
| mill-wheel.js | 从原画抽水轮、透视纹理重建与转动 | S/是 |
| movement.js | 输入积分、步态、职业姿态、IK与蒙皮变形 | D/是 |
| night.js | 昼夜渐变、窗灯、月星、人群退场、氛围合成 | D/是 |
| people-frames.js | 人物图集裁框生成表 | G/是 |
| people-masks.js | 人物遮罩坐标 | G/是 |
| population-v3-data.js | 唯一人物资产、可行走面与落脚修订 | G/否 |
| population.js | 街市居民/行人/职业/身高数据和居民姿态 | D/是 |
| river-boats.js | 扩展街区撑船角色分层、船行与歌女动作 | S/否 |
| scene-backdrop.js | 有余量的世界空间建筑缓存 | D/是 |
| scene.js | 主状态、输入、仿真循环、资源与最终合成 | D/是 |
| seasons.js | 异步季节控制器、服装/气候/落叶雪 | D/是 |
| sound.js | 录音与WebAudio事件声、空间声像和雨声ducking | D/是 |
| street-details.js | 故事道具绘制、交接物与风动细节 | S/是 |
| street-life.js | 六故事编排、避让clock、渡船货运协调 | D/是 |
| sunyang-actor.js | 店内画师Canvas人物与路径 | S/否 |
| sunyang-guests.js | 食客Canvas、落座和桌面菜肴布置 | S/否 |
| sunyang-host.js | 独立alpha店员、动作锚定 | S/否 |
| sunyang-sound.js | 店内音乐/拨弦点击、用户手势解锁 | S/否 |
| sunyang.js | 入店→选座→点餐→上菜流程与场景镜头 | S/否 |
| tea-study-actors.js | 茶摊局部对比的原角色图集/坐标 | S/否 |
| tea-study.js | 修改前/后/左右对比、拖拽缩放与载入失败恢复 | S/否 |
| visitors-data.js | 独立城市游客与尺寸修订数据 | G/否 |
| visits.js | 仅线上请求访问计数、3秒timeout | D/是 |
| wardrobe.js | 服装多边形、受保护区域、配色与季节工装 | S/是 |
| water.js | Canvas回退水面线条与扰动函数 | S/是 |
| weather.js | 雨阶段、伞、湿地/雨棚/雨线 | D/是 |
| workshop-actors.js | 季节作坊人物贴图裁切与抓握道具 | S/是 |
| world.js | 桥/台基路径、渡船阶段、乘客路径和环境行人 | D/是 |
12. 敦煌独立重建应该迁移什么
可复用的是方法,不是受限源文件或上游插画:
- 将“真壁画存档”与“动态艺术演绎”明确分层;原图只读保留,动态输出标注非文物复原。
- 每个被宣称“人物在动”的母题有自己的alpha层、关节/手指/飘带根部锚点、适当修补底板与遮挡。没有分层就不承诺原画人物完整动作。
- 大面积色层以浅深度/镜头视差处理;局部飘带/云气用二维网格或Shader,法器、手、头面保持不同权重,避免整身橡皮抖动。
- 统一scene clock;用户暂停时人物/尘粒/纹理动画全部停止。镜头用独立状态,滚动推进可逆且不能绑死自动播放。
- 真正的WebGL效果要有frame计数、可见像素变化、上下文丢失回退及一次真实浏览器录屏证据。不能用“script存在/测试通过/有Canvas标签”替代效果在动。
- 保留静态考据模式并不等于默认动态做完;首页必须直接看到可解释、可暂停的动态舞台,再进入高清资料库。
13. 权利与剩余未知
LICENSE:3-9明示公开查阅不授予复制/修改/分发/商用额外许可,因此独立重建不复制业务源码/画片。docs/ASSET-INVENTORY.md:7-10说明插画imagegen制作、音频来源授权不完整,Three.js MIT与字体OFL仅约束对应第三方组件。技术分析与商业素材复用是两件事。
待补:主审的crossing/Three水GLSL深审;实际部署dist与固定提交的一致性;真实GPU截图/录屏、低端移动机、音频实际播放及设备热衰减;大数据坐标/每个素材授权;上游全部测试实跑。未有证据前不将这些状态写为“完整验证完成”。