mcpField
T
threejs-scene-setup

threejs-scene-setup

github.com/gamedev-skills开发5.4k6.2k 次安装今天更新
代码仓库

关于 threejs-scene-setup

为 three.js 应用搭建场景基础的技能,面向初始化和排查渲染问题的开发者。内容包括模块加载、场景与相机、渲染循环、响应式调整和 OrbitControls。

主要功能

  1. 01配置 ES 模块加载
  2. 02创建场景、相机和渲染器
  3. 03设置动画渲染循环
  4. 04适配画布尺寸变化
  5. 05添加 OrbitControls

适用场景

  1. 01初始化 three.js 场景
  2. 02修复空白或黑色画布
  3. 03实现窗口调整时的画面适配
  4. 04为开发场景添加轨道控制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill threejs-scene-setup

安装后在 Claude Code 中输入/threejs-scene-setup确认已加载。

附带文件references/scene-graph.md

详细介绍

three.js 场景搭建

搭建 three.js 应用的基础:模块加载、场景/相机/渲染器三件套、渲染循环、自适应尺寸和相机控件。此处的模式面向 r186。修改现有项目之前,请先检查已安装的 three 版本,因为示例和附加组件会随版本变化。

适用场景

  • 在初始化 three.js 场景、修复空白/黑色画布、让画布自适应尺寸、设置动画循环或添加 OrbitControls 时使用。
  • 在 package.json 依赖 three,且代码中使用 import * as THREE from 'three' 时使用。

不适用场景: 加载 .gltf/.glb 模型或骨骼动画 → threejs-gltf-loading。材质、灯光、阴影、环境贴图 → threejs-materials-lighting。2D 渲染 → pixijs-rendering。

核心工作流

  1. 使用导入映射表将 three.js 作为 ES 模块加载。 从 r147 开始,裸模块说明符 'three' 和 'three/addons/' 必须进行映射(在 HTML 中或通过打包器)。附加组件(控件、加载器)位于 three/addons/... 下。
  2. 创建三件套。 Scene(场景图的根节点)、向原点后方移动的 PerspectiveCamera(fov, aspect, near, far),以及 domElement 位于 DOM 中的 WebGLRenderer。设置尺寸和 pixelRatio。
  3. 添加网格。 使用 new Mesh(geometry, material) 并调用 scene.add(mesh)。使用受光照影响的材质时,还需要灯光(参见 threejs-materials-lighting)。
  4. 使用 renderer.setAnimationLoop(fn) 驱动渲染循环。 这是手动编写 requestAnimationFrame 的现代替代方案,且适用于 WebXR/WebGPU。使用 Timer 获取时间增量(自 r183 起,Clock 已弃用)。
  5. 处理尺寸调整, 让相机宽高比和渲染器与画布匹配;更新 camera.aspect,调用 updateProjectionMatrix(),并调用 renderer.setSize(...)。
  6. 添加 OrbitControls, 以便在开发时旋转/平移/缩放。确认确实有内容渲染出来(受光照的立方体、能够响应的控件),再判断是否成功。

模式

1. HTML 导入映射表 + 模块入口(不使用打包器)
<canvas id="c"></canvas>
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
  }
}
</script>
<script type="module" src="./main.js"></script>

使用打包器(Vite/webpack)时,跳过导入映射表,直接运行 npm i three;相同的 import 语句即可解析。

2. 场景 + 相机 + 渲染器
// main.js
import * as THREE from 'three';

const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // cap for perf
renderer.setSize(window.innerWidth, window.innerHeight);

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x101018);

const camera = new THREE.PerspectiveCamera(
  60,                                   // vertical field of view (degrees)
  window.innerWidth / window.innerHeight, // aspect
  0.1,                                  // near
  100                                   // far
);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshNormalMaterial()        // unlit; shows orientation without a light
);
scene.add(cube);
3. 渲染循环(setAnimationLoop + Timer)
const timer = new THREE.Timer();        // replaces the deprecated THREE.Clock (r183+)
timer.connect(document);                // optional: no huge delta after a hidden tab

renderer.setAnimationLoop((time) => {
  timer.update(time);                   // call once per frame, before getDelta()
  const dt = timer.getDelta();          // seconds since last frame
  cube.rotation.x += dt;                // frame-rate independent
  cube.rotation.y += dt * 0.7;
  renderer.render(scene, camera);
});
// renderer.setAnimationLoop(null); // stop the loop
4. 自适应尺寸调整
function onResize() {
  const w = window.innerWidth, h = window.innerHeight;
  camera.aspect = w / h;
  camera.updateProjectionMatrix();      // required after changing aspect
  renderer.setSize(w, h);
}
window.addEventListener('resize', onResize);
5. OrbitControls(旋转/平移/缩放)
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;          // inertial feel
controls.target.set(0, 0, 0);

renderer.setAnimationLoop(() => {
  controls.update();                    // needed every frame when damping is on
  renderer.render(scene, camera);
});

常见问题

  • Failed to resolve module specifier "three" → 缺少导入映射表(或打包器配置)。映射 "three" 和 "three/addons/";附加组件路径必须以 / 结尾。
  • 画布全黑,没有报错 → 相机位于原点(在物体内部或后方),或者使用了受光照影响的材质(MeshStandardMaterial),却没有灯光。将相机向后移;先使用 MeshNormalMaterial/MeshBasicMaterial 验证几何体。
  • 没有任何动画 → 没有在循环中调用 renderer.render,或者调用了 setAnimationLoop,却在循环外渲染。
  • 调整尺寸后画面被拉伸/压扁 → 调整了渲染器尺寸,却没有更新 camera.aspect 并调用 updateProjectionMatrix()。
  • HiDPI 屏幕上画面模糊或锯齿明显 → 设置 renderer.setPixelRatio(...);将其限制在约 2,避免 4K/Retina 屏幕导致性能骤降。
  • OrbitControls 没有响应 → 设置 enableDamping = true 后,必须在每一帧调用 controls.update()。
  • 旧教程使用 <script src="three.min.js"> → 自 r147 起,three.js 仅提供 ES 模块;请使用 type="module" + 导入映射表。

参考资料

  • 关于坐标约定、场景图(Group、父子变换、Object3D 的添加/移除)、用于 2.5D 的 OrthographicCamera,以及如何释放几何体/材质/纹理以避免内存泄漏,请阅读 references/scene-graph.md。

相关技能

  • threejs-materials-lighting — 为表面添加受光照影响的外观(灯光、阴影、PBR)。
  • threejs-gltf-loading — 加载 3D 模型并播放其动画。
  • pixijs-rendering — 在浏览器中进行 2D 渲染。
  • fps-shooter — 组合 three.js 技能的 3D 游戏类型模板。

评论

支持 Markdown