Three.js
Three.js 是一个跨浏览器的 JavaScript 3D 图形库,基于 WebGL/WebGPU 渲染。当前最新版本约 r163+。推荐通过 npm 安装使用。
安装与引入
bash
npm install threenpm install threejs
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')
Cao Note