- Published on
THREE.js 知识: 搭建场景
- Authors

- Name
- 王凯
1. 光源
| 光源类型 | 构造函数 | 方向 | 范围衰减 | 投影阴影 | 常用用途 |
|---|---|---|---|---|---|
| 环境光 | THREE.AmbientLight | ❌ | ❌ | ❌ | 没有方向性,所有物体受到均匀照亮。通常用于打底照明,防止阴影完全黑 |
| 方向光 | THREE.DirectionalLight | ✅ | ❌ | ✅ | 模拟太阳光,从一个方向照射整个场景 |
| 点光源 | THREE.PointLight | ❌ | ✅ | ✅ | 从一点向四周发射光线,类似灯泡 |
| 聚光灯 | THREE.SpotLight | ✅ | ✅ | ✅ | 有方向、有范围、有角度,适合舞台灯、聚焦灯等 |
| 平行光 | THREE.HemisphereLight | ❌ | ❌ | ❌ | 顶部天空色 + 底部地面色的混合光,模拟自然环境光 |
矩形区域光 | THREE.RectAreaLight | ✅ | ✅ | ❌ | 模拟面光源,面光源、窗户、广告牌光 |
环境光
方向光
点光源
聚光灯
平行光
矩形区域光
2. 摄像机
名称 | 相机类型 | 用途 | 自动更新 | 透视效果 | 特点/限制 |
|---|---|---|---|---|---|
| 透视摄像机 | PerspectiveCamera | 常规 3D 场景、人眼视角模拟 | ❌ | ✅ | 默认最常用 |
| 正交摄像机 | OrthographicCamera | 2D、CAD、建筑、UI、工程图 | ❌ | ❌ | 无透视畸变 |
| 立方体摄像机 | CubeCamera | 生成环境贴图、反射镜面等 | ✅ | ✅ | 6 向拍摄 |
| 数组摄像机 | ArrayCamera | 多视图、分屏、立体视觉 | ❌ | ✅ | 可用于 VR 眼睛等 |
| WebXR摄像机 | WebXRCamera | WebXR 模式(如 VR 设备)自动创建 | ✅ | ✅ | 头显专用,自动控制 |
const rgbeLoader = new RGBELoader()
rgbeLoader.load('/material/city/san_giuseppe_bridge_2k.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.background = texture
})
// 添加一个动态反射的球体
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, {
format: THREE.RGBAFormat,
generateMipmaps: true,
minFilter: THREE.LinearMipmapLinearFilter,
})
const cubeCamera = new THREE.CubeCamera(1, 1000, cubeRenderTarget)
scene.add(cubeCamera)
const sphereGeo = new THREE.SphereGeometry(50, 32, 32)
const reflectiveMaterial = new THREE.MeshBasicMaterial({
envMap: cubeRenderTarget.texture,
})
const sphere = new THREE.Mesh(sphereGeo, reflectiveMaterial)
scene.add(sphere)
// 动画循环
function r() {
cubeCamera.position.copy(sphere.position)
cubeCamera.update(renderer, scene)
renderer.render(scene, camera)
requestAnimationFrame(animate)
}
r()
立方体摄像机
3. 渲染器
| 渲染器 | 特点 | 使用场景 |
|---|---|---|
WebGLRenderer | GPU 加速渲染,支持光照/阴影/后处理 | 通用、高性能三维场景 |
SVGRenderer | 矢量图,适合打印和技术图纸 | 静态展示、输出为 SVG |
CSS2DRenderer | HTML 元素叠加在屏幕上,不受透视变形影响 | 标签、信息面板 |
CSS3DRenderer | HTML 元素 + 3D 空间,DOM 可交互 | 3D UI、地图弹窗等 |
WebGPURenderer | 下一代 WebGPU 技术 | 高端设备/性能极限应用 |
/**
* CSS2DRenderer 与 CSS3DRenderer 代码逻辑一致
* 使用 CSS2DRenderer 时,将下面代码的 3D 替换成 2D 即可
*/
const ele = document.createElement('div')
ele.innerHTML = '<p style="background:#fff;padding: 10px;">3D Object</p>'
const obj = new CSS3DObject(ele)
obj.position.y = 80
scene.add(obj)
const css3Renderer = new CSS3DRenderer()
css3Renderer.setSize(width, height)
css3Renderer.domElement.style.position = 'absolute'
css3Renderer.domElement.style.pointerEvents = 'none'
const div = document.createElement('div')
div.style.position = 'relative'
div.appendChild(css3Renderer.domElement)
c.appendChild(div)
const r = () => {
css3Renderer.render(scene, camera)
renderer.render(scene, camera)
requestAnimationFrame(r)
}
r()
CSS2DRenderer
CSS3DRenderer
4. 控制器
| 控制器名称 | 功能描述 | 鼠标操作 | 动画轨迹 | 常见用途 |
|---|---|---|---|---|
OrbitControls | 环绕中心点旋转(查看模型/场景) | 左旋转 / 中平移 / 滚缩放 | ❌ | 最常用,用于查看模型或场景 |
TrackballControls | 类似 3D 软件的自由旋转,轨迹球控制 | 任意角度旋转 | ❌ | 更自由的交互体验,适用于设计类工具 |
FlyControls | 飞行模式自由移动 | 需要键盘+鼠标 | ❌ | 飞行浏览大型场景,如城市、地形 |
FirstPersonControls | 第一人称控制器(如游戏视角) | 鼠标看方向,键盘移动 | ❌ | 游戏场景、VR 预览等 |
PointerLockControls | 游戏式第一人称控制,鼠标锁定 | 鼠标锁定视角,键盘控制移动 | ❌ | FPS 游戏、沉浸式体验 |
DragControls | 拖拽物体 | 鼠标点击拖动 | ❌ | 拖拽交互、编辑器场景 |
TransformControls | 缩放/旋转/移动 Gizmo 控制器 | 鼠标控制轴向编辑 | ❌ | 编辑器工具,比如 Blender 样式操作 |
MapControls | 类似地图的操作方式 | 平移/缩放(类似 Orbit,但没有旋转) | ❌ | GIS 地图交互、2D 场景 |
ArcballControls | 鼠标中心点轨迹球旋转 | 点击选中轴旋转 | ✅ | 复杂三维交互,建模软件 |
DeviceOrientationControls | 使用移动设备陀螺仪控制视角 | 设备自动 | ✅ | AR、移动设备浏览场景 |
4.1 常用控制器对比
| 控制器 | 支持平移 | 支持旋转 | 支持缩放 | 支持拖拽物体 | 常用组合 |
|---|---|---|---|---|---|
OrbitControls | ✅ | ✅(围绕目标) | ✅ | ❌ | 默认相机控制 |
TransformControls | ❌ | ✅(操作物体) | ✅ | ✅ | 配合 Orbit 使用 |
DragControls | ✅(拖动) | ❌ | ❌ | ✅ | 可与 Transform/Orbit 结合 |
PointerLockControls | ❌(移动场景) | ✅(视角) | ❌ | ❌ | 第一人称、游戏 |
FirstPersonControls | ✅ | ✅ | ❌ | ❌ | 走动式浏览体验 |
FlyControls | ✅ | ✅ | ✅ | ❌ | 飞行自由漫游场景 |
new DragControls([box], camera, renderer.domElement)
const transformControls = new TransformControls(camera, renderer.domElement)
transformControls.attach(box)
scene.add(transformControls.getHelper())
DragControls
TransformControls
5. 资源加载
加载器 | 加载类型 | 类名 | 文件格式 | 是否异步 |
|---|---|---|---|---|
| 贴图加载器 | 图片纹理 | THREE.TextureLoader | .jpg, .png, .webp 等 | ✅ |
| 立方体贴图加载器 | 环境贴图 | THREE.CubeTextureLoader | 六面图 | ✅ |
| HDR 环境贴图 | HDR 贴图 | RGBELoader | .hdr | ✅ |
| GLTF 模型加载器 | 模型/动画/贴图 | GLTFLoader | .gltf, .glb | ✅ |
| FBX 模型加载器 | 模型/动画 | FBXLoader | .fbx | ✅ |
| OBJ 模型加载器 | 模型 | OBJLoader | .obj | ✅ |
| MTL 材质加载器 | 材质描述文件 | MTLLoader | .mtl | ✅ |
| 字体加载器 | 字体数据 | FontLoader | .json | ✅ |
| 音频加载器 | 音频文件 | AudioLoader | .mp3, .ogg 等 | ✅ |
| 视频纹理 | 视频流 | VideoTexture | .mp4, .webm 等 | ❌ |
| Draco 解码器 | 压缩 GLTF | DRACOLoader | .drc、压缩的 gltf/glb | ✅ |
| KTX2 加载器 | 压缩贴图 | KTX2Loader | .ktx2 | ✅ |
// 贴图加载器
const loader = new THREE.TextureLoader()
const texture = loader.load('/static/images/heart.png')
const boxGeo = new THREE.BoxGeometry(60, 60, 60)
const material = new THREE.MeshStandardMaterial({
color: 'rgb(180,180,180)',
map: texture,
})
scene.add(new THREE.Mesh(boxGeo, material))
// 立方体贴图加载器
const textureLoader = new THREE.CubeTextureLoader()
const texture = textureLoader
.setPath('/material/forest/')
.load(['nx.png', 'ny.png', 'nz.png', 'px.png', 'py.png', 'pz.png'])
scene.background = texture
// HDR 环境贴图
const rgbeLoader = new RGBELoader()
rgbeLoader.load('/material/city/san_giuseppe_bridge_2k.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.background = texture
})
/**
* GLTF 模型加载器
* Draco 解码器
*/
const loader = new GLTFLoader()
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')
loader.setDRACOLoader(dracoLoader)
loader.load('/model/angryBird/scene.glb', (gltf) => {
gltf.scene.scale.set(180, 180, 180)
scene.add(gltf.scene)
})
// 音频加载器
const listener = new THREE.AudioListener()
const audio = new THREE.Audio(listener)
const audioLoader = new THREE.AudioLoader()
audioLoader.load('/audio/superman.mp3', (buffer) => {
audio.setBuffer(buffer)
})
audio.play()
贴图加载器
立方体贴图加载器
HDR 环境贴图
GLTF 模型加载器
音频加载器
7. 创建 Mesh
THREE.js 知识: 创建 Mesh
6. GUI 控制
| 方法 | 作用 |
|---|---|
new GUI(options) | 创建 GUI 面板实例 |
gui.add(obj, prop, [min], [max], [step]) | 添加控制条(滑块、开关、输入框等) |
gui.addColor(obj, prop) | 添加颜色选择器 |
gui.addFolder(name) | 创建子面板(折叠) |
gui.destroy() | 销毁整个 GUI 面板,释放资源 |
gui.hide() / gui.show() | 显示或隐藏面板 |
gui.title() | 设置面板标题 |
gui.close() / gui.open() | 折叠/展开面板 |
const gui = new GUI()
gui.title('GUI 控制')
const boxGui = gui.addFolder('box')
boxGui.addColor(box.material, 'color')
boxGui
.add({ size: 80 }, 'size')
.name('滑块')
.min(50)
.max(150)
.step(1)
.onChange((size) => {
scene.remove(box)
box = createBox(size)
scene.add(box)
})
boxGui.close()
setTimeout(() => {
boxGui.open()
}, 1000)
gui.domElement.style.position = 'absolute'
div.appendChild(gui.domElement)
8. 小结(场景搭建)
const c = ref.current!
const scene = new THREE.Scene()
const renderer = new THREE.WebGLRenderer({ antialias: true })
const x = window.innerWidth / window.innerHeight
const width = c.clientWidth
const height = width / x
renderer.setSize(width, height)
const axesHelper = new THREE.AxesHelper(20)
scene.add(axesHelper)
// 灯光不影响 AxesHelper 展示
const light = new THREE.PointLight(0xffffff, 100000)
light.position.set(200, 200, 200)
scene.add(light)
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000)
camera.position.set(200, 0, 0)
camera.lookAt(0, 0, 0)
new OrbitControls(camera, renderer.domElement)
c.appendChild(renderer.domElement)
const render = () => {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
render()