Skip to content

Three.js ​

Three.js 是一个跨浏览器的 JavaScript 3D 图形库,基于 WebGL/WebGPU 渲染。当前最新版本约 r163+。推荐通过 npm 安装使用。

安装与引入 ​

bash
npm install three
npm install three
js
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

CDN 方式(importmap):

html
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three/examples/jsm/"
  }
}
</script>
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three/examples/jsm/"
  }
}
</script>

核心概念 ​

概念说明
Scene(场景)所有物体的容器
Camera(相机)决定观察视角(PerspectiveCamera / OrthographicCamera)
Renderer(渲染器)将场景渲染到画布上(WebGLRenderer / WebGPURenderer)
Mesh(网格)几何体 + 材质的组合
Light(光源)影响物体显示效果
Controls(控制器)用户交互控制(OrbitControls、DragControls 等)

完整场景模板 ​

js
// 1. 场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x111122)

// 2. 相机
const camera = new THREE.PerspectiveCamera(
  45, window.innerWidth / window.innerHeight, 0.1, 1000
)
camera.position.set(5, 5, 5)

// 3. 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true
document.body.appendChild(renderer.domElement)

// 4. 光源
const ambient = new THREE.AmbientLight(0x404060)
scene.add(ambient)
const dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.position.set(10, 20, 10)
scene.add(dirLight)

// 5. 物体
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x44aa88, roughness: 0.4 })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

// 6. 辅助
scene.add(new THREE.AxesHelper(3))
scene.add(new THREE.GridHelper(10, 10))

// 7. 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

// 8. 动画循环
function animate() {
  requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
}
animate()

// 9. 响应式
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})
// 1. 场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x111122)

// 2. 相机
const camera = new THREE.PerspectiveCamera(
  45, window.innerWidth / window.innerHeight, 0.1, 1000
)
camera.position.set(5, 5, 5)

// 3. 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true
document.body.appendChild(renderer.domElement)

// 4. 光源
const ambient = new THREE.AmbientLight(0x404060)
scene.add(ambient)
const dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.position.set(10, 20, 10)
scene.add(dirLight)

// 5. 物体
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x44aa88, roughness: 0.4 })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

// 6. 辅助
scene.add(new THREE.AxesHelper(3))
scene.add(new THREE.GridHelper(10, 10))

// 7. 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

// 8. 动画循环
function animate() {
  requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
}
animate()

// 9. 响应式
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

添加相机 ​

jsx
// 创建场景
const scene = new THREE.Scene()

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面
  1000, // 远平面
)

// 创建渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// 渲染函数
function animate() {
  controls.update()
  requestAnimationFrame(animate)
  // 旋转
  // cube.rotation.x += 0.01;
  // cube.rotation.y += 0.01;
  // 渲染
  renderer.render(scene, camera)
}
animate()
// 创建场景
const scene = new THREE.Scene()

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面
  1000, // 远平面
)

// 创建渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// 渲染函数
function animate() {
  controls.update()
  requestAnimationFrame(animate)
  // 旋转
  // cube.rotation.x += 0.01;
  // cube.rotation.y += 0.01;
  // 渲染
  renderer.render(scene, camera)
}
animate()

创建几何体 ​

// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建网格
const cube = new THREE.Mesh(geometry, material);
// 将网格添加到场景中
scene.add(cube);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建网格
const cube = new THREE.Mesh(geometry, material);
// 将网格添加到场景中
scene.add(cube);

控制器和辅助坐标系 ​

jsx
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)

// 添加轨道控制器
const controls = new OrbitControls(camera, document.body)
// 设置带阻尼的惯性
controls.enableDamping = true
// 设置阻尼系数
controls.dampingFactor = 0.05
// 设置旋转速度
controls.autoRotate = true
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)

// 添加轨道控制器
const controls = new OrbitControls(camera, document.body)
// 设置带阻尼的惯性
controls.enableDamping = true
// 设置阻尼系数
controls.dampingFactor = 0.05
// 设置旋转速度
controls.autoRotate = true

光线投射实现 3d 物体交互 ​

jsx
// 创建三个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  }),
)
sphere1.position.x = -4
scene.add(sphere1)
//创建射线
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
window.addEventListener('click', (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  raycaster.setFromCamera(mouse, camera)
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3])
  console.log(intersects)
  if (intersects.length > 0) {
    if (intersects[0].object.material.isColor) {
      intersects[0].object.material.color.set(intersects[0].object.material.ot_color)
      intersects[0].object.material.isColor = false
      return
    }
    intersects[0].object.material.isColor = true
    intersects[0].object.material.ot_color = intersects[0].object.material.color.getHex()
    intersects[0].object.material.color.set(0xff0000)
    console.log(intersects[0].object.material)
  }
})
// 创建三个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  }),
)
sphere1.position.x = -4
scene.add(sphere1)
//创建射线
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
window.addEventListener('click', (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  raycaster.setFromCamera(mouse, camera)
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3])
  console.log(intersects)
  if (intersects.length > 0) {
    if (intersects[0].object.material.isColor) {
      intersects[0].object.material.color.set(intersects[0].object.material.ot_color)
      intersects[0].object.material.isColor = false
      return
    }
    intersects[0].object.material.isColor = true
    intersects[0].object.material.ot_color = intersects[0].object.material.color.getHex()
    intersects[0].object.material.color.set(0xff0000)
    console.log(intersects[0].object.material)
  }
})

补间动画 ​

jsx
// 创建GUI
const gui = new GUI()

// 创建1个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  }),
)
sphere1.position.x = -4
scene.add(sphere1)

// 创建补间动画
const tween = new TWEEN.Tween(sphere1.position)
  .to({ x: 4, y: 4 }, 1000) //移动到哪里,持续时间
  .easing(TWEEN.Easing.Quadratic.InOut) //动画效果
  .repeat(Infinity) //执行次数
  .yoyo(true) // 返回动画
  .start() // 启动补间动画
//钩子函数
tween.onStart(() => {
  console.log('开始')
})
tween.onComplete(() => {
  console.log('结束')
})
tween.onStop(() => {
  console.log('停止')
})
tween.onUpdate(() => {
  console.log('更新')
})
let fnObj = {
  stop: () => {
    tween.stop()
  },
  start: () => {
    tween.start()
  },
}
gui.add(fnObj, 'stop')
gui.add(fnObj, 'start')
// 创建GUI
const gui = new GUI()

// 创建1个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  }),
)
sphere1.position.x = -4
scene.add(sphere1)

// 创建补间动画
const tween = new TWEEN.Tween(sphere1.position)
  .to({ x: 4, y: 4 }, 1000) //移动到哪里,持续时间
  .easing(TWEEN.Easing.Quadratic.InOut) //动画效果
  .repeat(Infinity) //执行次数
  .yoyo(true) // 返回动画
  .start() // 启动补间动画
//钩子函数
tween.onStart(() => {
  console.log('开始')
})
tween.onComplete(() => {
  console.log('结束')
})
tween.onStop(() => {
  console.log('停止')
})
tween.onUpdate(() => {
  console.log('更新')
})
let fnObj = {
  stop: () => {
    tween.stop()
  },
  start: () => {
    tween.start()
  },
}
gui.add(fnObj, 'stop')
gui.add(fnObj, 'start')