关于 threejs-scene
threejs-scene 是一项技能和起始项目,用于通过 three.js 构建基于实际尺寸的 3D 场景。它引导开发者从参考尺寸和几何建模,逐步完成照明、性能检查、响应式视角及逐帧精确录制。
主要功能
- 01包含渲染器和照明设置的起始场景
- 02重复部件与扫掠几何建模模式
- 03与天空匹配的照明预设
- 04摄像机视点与动画过渡
- 05性能检查与逐帧精确录制
适用场景
- 01构建按尺寸建模的建筑 3D 场景
- 02通过实例化创建重复几何体
- 03调整不同预设下的场景照明
- 04检查并录制 three.js 场景
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ moh4696/ open-3d-skills --skill threejs-scene安装后在 Claude Code 中输入/threejs-scene确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlassets/starter/env.jsindex.htmlkit.jsmain.jsscene.jsreferences/geometry.mdlighting.mdperformance.mdshaders.md详细介绍
three.js 场景
令人信服的 3D 页面,关键在于光线和比例,而非多边形数量。白宫之所以看起来像白宫,是因为它的楼层高度、柱间距和檐口深度都恰到好处,也因为低角度的阳光在建筑上投下长长的阴影,而不是因为每扇窗户都有窗框。这项技能会像制作建筑模型一样构建场景:基于真实尺寸,用代码实现,并妥善处理光照。
从起始模板开始
assets/starter/ 是一个完整可用的场景(草坪上的圆形凉亭),下文提到的内容都已接好。复制它,保留 env.js、kit.js 和 main.js,然后用你的主题替换 scene.js。
cp -r assets/starter my-scene && cd my-scene
python3 -m http.server 8000 # any static server; ES modules need http://, not file://
| 文件 | 内容 |
|---|---|
index.html | 固定使用 three r169([email protected])的导入映射、画布、标题、光照按钮、加载器 |
main.js | 渲染器、摄像机、后期处理栈、质量档位、视角和运镜、录制钩子、帧循环 |
env.js | 天空、太阳、基于图像的光照、雾、云、夜空;三个可混合的预设;阴影适配 |
kit.js | 带种子的随机数生成器、画布纹理、实例化工具包、旋转成形、弧线、带斜接角的轮廓扫掠 |
scene.js | 示例主题和所有值得借鉴的模式 |
URL 参数开箱即用:?view=front|corner|low|aerial、?light=morning|golden|night、?quality=high|balanced|fast,以及使用 frame-capture 进行帧精确录制的 ?record=1。
工作流
- 开始编写几何体前,先收集参考资料和尺寸。 准备平面图、立面图、各侧照片以及真实尺寸(楼层高度、开间间距、柱径、轴距)。将它们以米为单位写成场景文件顶部的常量。按猜测比例搭建的建筑无论多精细,看起来都像玩具。
- 先用方块搭建整体结构,确保尺寸正确,并从访客会看到的视角检查。现在就修正比例;细节掩盖不了问题。
- 按类型添加细节,而不是逐个制作。 将一根柱子、一扇窗户、一个栏杆柱写成函数,然后通过
Kit放置数百个实例(每个部件一次绘制调用)。线脚、路缘、台阶和栏杆都是沿路径扫掠的轮廓(sweep);圆形物体则用旋转成形制作。 - 添加光照,使用
env.js中的预设,然后调整曝光、太阳高度和雾效来营造氛围。大部分真实感都来自这一步。 - 增添生气: 为树木添加风动效果、水面、旗帜、行人或车流等实例化精灵,以及夜间亮起的灯。所有效果都由共享 uniforms(
U.uTime、U.uWind、U.uNight)驱动,确保录制和光照变化保持同步。 - 测量并检查:使用
site-check检查帧时间、绘制调用、每种光照下的每个视角,以及手机端效果。然后用frame-capture录制。
重要的渲染设置
这些设置位于 main.js 中;除非你知道原因,否则请保留。
- 色调映射 使用
ACESFilmicToneMapping,并根据预设设置曝光(0.6 到 1.0)。不使用色调映射时,明亮的天空会过曝,整个画面看起来像 2009 年的游戏。 - 在合成器中抗锯齿,而非在画布中: 渲染器设置为
antialias: false,并为效果合成器使用samples: 4的HalfFloatType渲染目标。启用后期处理后,画布 MSAA 不起作用。 - 后期处理堆栈:
RenderPass→GTAOPass(环境光遮蔽:为飞檐下方、柱子之间、墙地交界处添加接触阴影)→UnrealBloomPass(白天强度约为 0.12,夜间为 0.5)→OutputPass(最后进行色调映射和 sRGB 转换)。GTAO 通道通过子类化来隐藏任何带有userData.noAO的内容(天空层、粒子、玻璃),因为 AO 会使其看到的任何内容变暗。 - 以天空本身为基础的图像光照:
env.js会将天空的私有副本渲染到 PMREM 环境贴图中,因此在每个预设下,反射和环境光的颜色都与天空相匹配。预设渐变期间重新生成环境贴图即可(每六帧一次就够了:PMREM 开销很大)。 - 只有一个投射阴影的太阳, 使用 4096 分辨率的贴图,其视锥体根据视野范围设置并以相机目标为中心,再对齐到整像素位置,避免相机移动时阴影闪烁。根据场景缩放的
normalBias可消除阴影痤疮,同时避免阴影悬浮。 - 长焦镜头: 视野为 38。在高屏幕上,启动代码会增大垂直视野,使水平视野与 16:9 屏幕相匹配;手机上也能看到完整主体,而不是被裁切的画面。
- 像素比上限为 2。 3x 手机渲染的像素数是 2x 的 2.25 倍,却没有可见收益。
光照
env.js 中的每个预设都会同步调整天空、太阳、半球光、雾、曝光和云。调整前请阅读 references/lighting.md。简要说明如下:
- 低角度的太阳(5 到 10 度)最能让场景显得精致:长阴影、暖色的立面和渐变天空。将光源设置在绘制的太阳上方几度,这样立面仍然清晰可见。
- 夜晚主要是缺少光照。将太阳强度保持在 0.15 左右,让窗户和灯具通过自发光材质与辉光效果照亮场景,并尽量少添加真实光源。点光源和聚光灯使用物理单位(坎德拉),并设置
decay: 2。如果将衰减设为 1.4,再随意猜测强度,单盏喷泉灯就会让半个场景过曝。 - 每盏暗光源仍会产生阴影渲染开销。强度为零时隐藏光源(
visible = false)。
几何图案
详情和代码请参阅 references/geometry.md。最常用的图案如下:
sweep(profile, path, { closed, y }):沿 XZ 平面上的路径扫掠由[向外偏移量, 高度]对组成的 2D 轮廓,并在拐角处进行斜接。适用于飞檐、檐部、路缘、道路边缘、台阶、踢脚线、画轨。闭合路径会自动判断绕行方向,因此“向外”始终指向外侧。lathe([[r, y], ...]):适用于带有柱身收分的柱子、栏杆柱、瓮、穹顶、灯柱、车轮和瓶子。Kit:只需定义一次部件,使用add添加矩阵,再用build为每个部件构建一个InstancedMesh。每个实例都可设置颜色,从而以零成本实现变化(叶片的绿色、砖块色调)。canvasTex(w, h, draw):使用设定种子的 RNG 在代码中绘制纹理。适用于石材斑点、草地、碎石、标牌和窗格网格。无需下载,无许可问题,每次加载的结果都相同。- 对于重复次数不足以使用实例化的静态几何体,如果它们共用一种材质,可将其合并(
BufferGeometryUtils.mergeGeometries)。
着色器补丁
使用 onBeforeCompile 扩展内置材质,而不是从头编写着色器;这样就能直接使用灯光、阴影和雾效。references/shaders.md 提供了经过测试的补丁,涵盖:随风摆动、朝上表面的积雪、季节性植被色调、夜间亮起的窗户、水面波纹。
容易忽略的一条规则:移动顶点的补丁也必须修改阴影渲染阶段。 为网格指定一个 customDepthMaterial(使用 RGBADepthPacking 的 MeshDepthMaterial),并让它携带相同的 onBeforeCompile,就像 scene.js 为树木所做的那样。否则,树叶会随风摆动,阴影却静止不动。
性能
以 1440x900 分辨率下的笔记本电脑 GPU 为目标:每帧低于 10 ms,绘制调用少于 300 次,三角形少于 200 万个。references/performance.md 介绍了如何测量和优化。按收益从高到低排列:
- 对重复部件使用实例化(
Kit)。通常瓶颈是绘制调用,而不是三角形数量。 - 设置质量档位:
high(DPR 最高为 2、GTAO、4096 阴影)、balanced(DPR 1.5)、fast(DPR 1、无 GTAO、2048 阴影)。启动模板会在慢帧持续三秒后自动降低一个档位,且录制时不会降低。 - 不要渲染静止内容:如果没有动画且摄像机静止,就跳过
composer.render()。 - 对小型、远处和扁平物体关闭
castShadow。
摄像机
- 为每个值得查看的视图定义命名的观察点(位置加注视目标)。按钮、深层链接、静帧和录制都使用这些观察点。
- 观察点之间的飞行会在中点处升高,并采用缓入缓出(
main.js中的flyTo)。直接线性插值会穿过建筑物,感觉也很机械。 - 限制
OrbitControls:将maxPolarAngle设为略小于水平角度,设置合理的最小和最大距离,并开启阻尼。 - 启用
prefers-reduced-motion时,飞行会改为直接跳转,且不会有任何自动移动。
录制
?record=1 会隐藏 UI,并从 window.__recordFrame(t) 开始按页面时钟计时执行脚本化导览。任何延迟操作都必须使用 main.js 中的 later(secs, fn),而不是 setTimeout。使用 frame-capture 技能录制(<frame-capture> 是该技能的安装位置):
python3 <frame-capture>/scripts/record.py "http://127.0.0.1:8000/?record=1" tour.mp4 --seconds 20 --ready "window.__ready === true"
完成前检查
- 在笔记本电脑尺寸和手机上,检查每个观察点在每种灯光预设下的表现(
site-check)。 - 不得出现阴影粉刺、环绕观察时闪烁的阴影边缘,或表面交接处的深度冲突(将共面贴花偏移 1 到 2 cm)。
- 不得有物体悬空或下沉:从低角度观察点检查每个物体与地面的交接处。
- 帧耗时和绘制调用均符合预算。
- 如果主题是真实地点、物体或品牌,应将页面标注为非官方研究作品,且不要使用徽标。