cyber-cutaway · git:20261103.7cf0331 · 2026-11-03 · sha256 30f1c6976f33717d

cyber-cutaway git:20261103.7cf0331A

Immutable. This exact content never changes and is served at /api/v1/blob/30f1c6976f33717d.

---
name: cyber-cutaway
description: "做「构造剖解」类 3D 讲解网页:用 three.js 把机器/建筑/设备剖开,露出内部结构,配全息标注、聚焦导览与赛博朋克 UI。当用户要「结构讲解 / 剖面图 / 内部构造演示 / 3D 拆解网页 / 产品解剖图 / 高科技展示页」时使用。含完整可运行案例(赛博飞机 EV-07,demo/index.html 双击即跑)、file:// 零依赖方案、three.js r149 色彩管理陷阱与剖开交互的六个实测视觉陷阱。"
---

# 3D 构造剖解页(Cyber Cutaway)

做一类**把物体剖开讲内部结构**的 3D 网页:外壳按交互分解,内部构件分系统着色,配悬浮全息标注与部件导览。它和"加载一个 3D 模型"的差别在于:模型是程序化生成的(无外部资源),剖开是一等交互(不只是旋转查看),文字标注要能跟着相机读得清。

完整案例见 `demo/`(双击 `demo/index.html` 直接运行,无需服务器、无外部请求)。

## 一、硬约束:file:// 直开

讲解页经常被拷给别人看,加载方式是双击文件而不是 HTTP:

- **three.js 用 UMD 版本地化**(`vendor/three.min.js`,r149 约 596K),不要用 module + importmap——`file://` 下 ES module 的裸路径会被 CORS 拦
- **零外部请求**:无 CDN、无 fetch、无外链字体;贴图全部用 `CanvasTexture` 程序化生成
- 验收只有一条:`file:///…/index.html` 打开,控制台零报错

## 二、最重要的一个陷阱:色彩管理

three r149 默认 `ColorManagement.legacyMode = true`:十六进制颜色**不做 sRGB→线性转换**,每个深色材质被整体抬亮 2~3 倍,画面必然发灰发白、毫无对比——而且怎么调灯、调曝光都救不回来。

```js
// 入口第一行(在创建 renderer 之前)
if (THREE.ColorManagement) THREE.ColorManagement.legacyMode = false;
```

连带规则:

- CanvasTexture 若内容是人眼所见的颜色(贴图、文字),设 `t.encoding = THREE.sRGBEncoding`
- 金属材质的 F0 = `color × map`,**贴图基色太黑(#222 以下)金属必成死黑**;面板贴图基色抬到 #4a5663 一档,色相靠 material.color 拉
- 开了色彩管理后所有灯光强度要重配(方向光 ~2、环境 ~0.5、点光按距离平方衰减调),曝光 1.1 左右起步

## 三、程序化建模的套路

没有模型文件,几何全靠代码:

1. **截面放样**:定义 N 组 2D 截面控制点 → CatmullRom 插值 → 沿轴向 loft 成网格。机身用超椭圆截面(`|cosθ|^(2/n)`),机翼用 NACA 四位数翼型
2. **剖解 = 上下半壳**:蒙皮按截面角度 θ 分上下两半 loft,**共享接缝顶点**否则拼不拢;上壳抬升后形成"解剖图"开口
3. **镜像件必须翻索引**:复制几何到另一侧后绕序反了,法线朝内、正面被剔除,表现为"只剩一边/像玻璃罩"。写一个 `flipIndex(g)` 交换每三角形的第 2、3 个索引
4. **贴图色彩**:CanvasTexture 默认按线性解读会被提亮,务必 sRGBEncoding

## 四、剖开交互的六个视觉陷阱

| 陷阱 | 现象 | 解法 |
|---|---|---|
| 上盖只抬高一点 | 相机永远隔着壳,等于没剖开 | 抬到明显分离的高度,且**实心蒙皮直接淡出**,只留线框轮廓浮在空中——半透明壳会一直抢视线 |
| 机翼/大附件挡内部 | 俯视时整个剖面被盖住 | 剖开度 > 阈值时让附件材质 opacity 退到 0.1(幽灵壳) |
| 内部亮成一片 | 构件全是浅灰,没有主次 | 关系要倒过来:**骨架是暗的,内部光才是亮的**。隔框压到 #202a35 档、flatShading 出硬边,发光件(灯带/核心/管线)用 MeshBasicMaterial 跳出来 |
| 甲板一大块平面 | 内部像空仓库 | 加分仓缝(细暗条把平面切成一块块地板)+ 中央通道踏板 |
| 核心辉光糊成白团 | 中心一坨死白 | 辉光 sprite 缩小(scale 1.7/3.2 而不是 3.4/6.0)、去掉 depthTest:false、外层改品红色与内层青色拉开 |
| 相机水平看剖面 | 只看到一条缝 | `setOpen` 越过 50% 时自动把 phi 抬到 ~0.78(3/4 俯视),否则用户永远看不见甲板 |

## 五、全息标注

内部构件叫什么,必须写在画面里——这是"高科技感"最直接的信号:

- `SpriteMaterial` + CanvasTexture 画文字,`depthTest:false` 保证不被构件遮挡,初始 opacity 0
- 剖开度 > 30% 后淡入到 0.92;配一条从标签指向构件的 LineSegments 引线
- **注意把 labelMats 从 interior 转发到 ship 返回对象**,否则透明度永远 0,画面上毫无报错(案例里实测踩过)
- 文字用等宽字体 + 双行(主名 + 参数小字),青色发光

## 六、灯光与氛围

- 主光(冷白方向光 ~2.0)+ 品红补光(~0.3)+ 青色轮廓光(~0.85)+ 顶部柔光
- 环境贴图决定金属质感:`PMREMGenerator.fromEquirectangular` 处理一张程序化 Canvas 渐变(机库灯带 + 品红侧光),没有 envMap 的金属是死黑
- 内部点光跟随剖开度点亮:客舱冷青、动力舱品红,强度 1~2 档,别超 3 否则过曝
- 后处理不用 EffectComposer:CSS `filter: blur() saturate()` + `mix-blend-mode: screen` 的叠层做泛光,成本低、file:// 下稳

## 七、验收清单

1. `file://` 双击打开,控制台零报错
2. 剖开滑杆从 0 到 100%:上盖先分离后消失、线框轮廓留下、机翼退幽灵、标注浮现
3. 点击左侧部件索引:相机聚焦 + 对应材质 emissive 高亮 + 信息卡更新
4. 数东西:内部至少能一眼认出 6 种以上构件(隔框/甲板/座椅/管线/货箱/核心/航电…),认不出的就是建模不够
5. 软件渲染下 1080p ≥ 20fps 即可(真实 GPU 无压力);三角形 ~2.5 万、draw call < 100