# 山间铁路案例

用于铁路、道路、桥隧、沿路径运动对象及环境切换。参考 Techartist / iamtechartist 的 Mountain Railway Diorama（Yamaai）：https://github.com/iamtechartist/mountain-railway-diorama 。原 Demo：https://iamtechartist.github.io/mountain-railway-diorama/ 。学习版本 `6e8b0fbb806e486d7819b6d297b92f4ecf3d258b`，MIT，Copyright (c) 2026 Techartist。Three.js r185，WebGLRenderer、GLSL、onBeforeCompile；不要默认替换成 TSL/WebGPU。

这些是该项目的设计选择。复用代码时保留 LICENSE 与 Three.js 版权；不要推断原作者采用过何种 AI 工作流。

## 路径与地形

`world.js` 定义 12 个控制点的闭合 Catmull–Rom 曲线，类型 catmullrom，tension=.34，arcLengthDivisions=1800；通过 getPointAt/getTangentAt 共用弧长参数。

- 沿中心线采样 680 点，最近点距离作为地形压平、种植排除和铁路净空的近似输入。
- 底层山体是高斯山峰、弯曲山脊、三角函数细节；铁路普通区间压平，桥区保留峡谷，隧道区加厚覆盖层。
- 河流中心 `riverX(z)=16+6.2*sin(.060*z)+1.7*sin(.16*z)`。共享水位用 smoothstep 连接三个主要落差。
- 网格使用极坐标环，外边界由多频正弦扰动；侧面共用边界采样形成剖面。
- 隧道入口按马蹄形截面和前后平面裁切地形三角形；交点 `f=da/(da-db)` 插值位置、颜色、法线。洞壁再沿铁路扫掠。高度场本身无法表示洞穴。

复用时先替换路线和区段，再生成地形。不要把 680 点采样误称精确最近点或完整碰撞。

## 车辆与设施

`train.js` 车身由截面蒙皮生成，两端收窄；窗口所在段跳过外壳面片，玻璃用单层面片，车内有实际座位。钢轮用回转轮廓生成。各车厢按材质合批，转向架保留为动态组。

```text
L = route.getLength()
u = mod(s/L, 1)
A = route.getPointAt(mod((s_i-b)/L,1))
B = route.getPointAt(mod((s_i+b)/L,1))
C = (A+B)/2
T = normalize(B-A)
R = normalize(UP cross T)
N = normalize(T cross R)
Q = quaternion([R,N,T])
```

此项目 b=2.25，车厢间距8.3；各转向架按自己的路径切线构造世界旋转，再乘车厢旋转的逆转为局部姿态。模型没有真实轮轨接触、关节或悬挂模拟；车轮合在静态几何中，没有独立滚动更新。

进站速度限制来自 `sqrt(max(0,2*.57*(ahead-.06)))`；目标速度还受转弯角和用户倍率限制；加速上限.34、减速上限.68，停车6.2秒。这些单位是场景单位，并非真实列车标定。主循环将 dt 分到不超过.05秒的子步。

桥梁与轨道沿相同曲线扫掠，桥墩向地表采样；站台、屋基、灯脚采样实际地面。农舍还检查旋转后占地范围，地基补到水平基础。

## 植被

`vegetation.js` 分别生成杉、松、枫、竹，另有蕨、灌木、草、野花。枝干是逐渐收窄的管状轮廓，针叶是折叠锯齿叶簇，枫叶有五裂形状。

- 分布为带种子的候选采样 + 坡度/海拔/铁路/水岸/建筑/镜头排除 + 最小间距；并非严格 Poisson-disk 算法。
- 低频场控制树种与枫叶色块；少量 heroSites 手工参数化布局，保留主体周围空隙。
- 顶点属性 aFlex/aFlutter 分别表示整体弯曲和细碎摆动；实例 iWindState 保存每帧共享阵风。深度材质复用 botanicalDeform。
- 普通树进入近景距离46，退出54；主镜头树90；近景枝叶分开实例化，远景合并并保留主要外轮廓。实例位置与色板保持一致。

## 水体与环境

`river.js` 在每一横断面从中心向外粗查岸线，再二分9次；300纵向段、22横向段，aDepth=水位-地形。静态深度参与透明度、颜色及岸边浪幅衰减。

水流纹理由多个移动 value-noise 频段构成。顶点是两组小幅正弦波；片元法线额外强化波纹，系数并非严格顶点位移导数。16个障碍半径驱动岩脚泡沫与下游指数尾迹。

反射使用384×384 HalfFloat Reflector，平均参考水位位于下游 z=22；约10Hz，交互时8Hz。河流有多个水位，只在接近下游水平面的区域权重混合该反射，不可作为全河精确镜像。透明浅水显示实际河床，本项目没有 Tideglass 式深度折射离屏 pass。

`cascade.js` 四段附加水片覆盖三个主要落差，沿水流累计距离写 UV；水位坡度生成 aEnergy，前后坡度差生成 aImpact，以此调节条带、破碎透明度和撞击泡沫。150个 Points 水雾循环运动，非流体模拟。

`weather.js` 的8400条雨丝为单个 LineSegments；顶点 shader 循环下降，以seed阈值控制雨量，风控制斜率。局部雾是柔边 Points，车灯通过与列车前向的夹角点亮它；54片枫叶共用单网格，85个萤火虫为循环路径。

全场 wetness 以较慢的指数插值变化，石头、叶片、车身与建筑同时变暗并降低粗糙度；7块站台积水独立显示。没有空间湿度历史纹理，不要照搬海湾的 DataTexture 解释。

## 光照与镜头

四套模式同时控制主光、半球光、补光、雾、天空、局部灯、环境反射强度和水光。`alpha=1-exp(-1.5*dt)` 平滑模式，湿度使用该alpha的.45倍。背景是三层封闭山脊，分别与天空混色；天空是全屏射线方向着色器，噪声云/哈希星/角度月盘，非完整大气散射。

主光阴影2048，单侧车头阴影1024，站台缓存阴影512；并非每盏灯都实时投影。车头透明光锥、局部雾和 SpotLight 共同表现光束。

luminous.js：HalfFloat主视图 → 四分之一宽高亮区横向过滤 → 纵向过滤 → 主图+bloom → ACES近似映射与色彩输出。亮区用smoothstep(1.35,2.7,maxRGB)提取，避免黑位全被提亮。

cinematography.js 按列车进度决定桥/瀑布/河流/出洞/车站/神社镜头；至少驻留7.2秒，转场用五次 smootherstep，沿32分段中的内部点检查地形高度并抬高过渡路径。稳定镜头只有很慢的推进。竖屏用 max(1,1/aspect) 拉远镜头。手动拖动关闭自动与跟随。

## 复用检查

1. 共享中心线后核对桥隧区段，修改路径刷新弧长缓存。
2. 检查转弯时车身及转向架、隧道两端净空、屋基与道具落脚。
3. 比较昼/暮/雨/夜，确认所有光源、材质、天空与水体同步，湿度有合理滞后。
4. 对照近景和远景，确认LOD不跳色、阴影与风同相。
5. 看真实主镜头和竖屏；确认相机不会穿山，手动与自动交接可靠。
6. 对反射100/125ms、阴影50ms、像素比1～1.5及异步GPU计时分别测量；不要只计主视图draw calls。

原项目 verify.mjs 可复测模块依赖、有限几何、36条入口净空射线、六处道具支撑和250秒列车服务；不能替代实际 shader、浏览器画面及托管容器验证。
