Published on

THREE.js 知识: 搭建场景

Authors
  • avatar
    Name
    王凯
    Twitter

 

1. 光源

光源类型构造函数
方向
范围衰减
投影阴影
常用用途
环境光THREE.AmbientLight没有方向性,所有物体受到均匀照亮。通常用于打底照明,防止阴影完全黑
方向光THREE.DirectionalLight模拟太阳光,从一个方向照射整个场景
点光源THREE.PointLight从一点向四周发射光线,类似灯泡
聚光灯THREE.SpotLight有方向、有范围、有角度,适合舞台灯、聚焦灯等
平行光THREE.HemisphereLight顶部天空色 + 底部地面色的混合光,模拟自然环境光
矩形区域光
THREE.RectAreaLight模拟面光源,面光源、窗户、广告牌光

 

2. 摄像机

名称
相机类型
用途
自动更新
透视效果
特点/限制
透视摄像机PerspectiveCamera常规 3D 场景、人眼视角模拟默认最常用
正交摄像机OrthographicCamera2D、CAD、建筑、UI、工程图无透视畸变
立方体摄像机CubeCamera生成环境贴图、反射镜面等6 向拍摄
数组摄像机ArrayCamera多视图、分屏、立体视觉可用于 VR 眼睛等
WebXR摄像机WebXRCameraWebXR 模式(如 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. 渲染器

渲染器
特点
使用场景
WebGLRendererGPU 加速渲染,支持光照/阴影/后处理通用、高性能三维场景
SVGRenderer矢量图,适合打印和技术图纸静态展示、输出为 SVG
CSS2DRendererHTML 元素叠加在屏幕上,不受透视变形影响标签、信息面板
CSS3DRendererHTML 元素 + 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()

 

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())

 

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 解码器压缩 GLTFDRACOLoader.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()

 

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()