Three.js 快速入门
本章节将带你创建第一个 Three.js 3D 场景,通过实践快速上手 Three.js 开发。
准备工作
在开始之前,确保你已经:
- 安装了 Node.js(推荐 v14+)
- 了解了基本的 HTML/CSS/JavaScript
- 安装了 Three.js(参见 简介与安装)
第一个 Three.js 场景
完整示例
创建一个 HTML 文件,包含以下内容:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的第一个 Three.js 场景</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js'
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/controls/OrbitControls.js'
// 1. 创建场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x333333)
// 2. 创建相机
const camera = new THREE.PerspectiveCamera(
75, // 视野角度
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
)
camera.position.set(0, 0, 5)
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, // 开启抗锯齿
alpha: true // 透明背景
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.shadowMap.enabled = true // 开启阴影
document.body.appendChild(renderer.domElement)
// 4. 创建几何体和材质
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
})
const cube = new THREE.Mesh(geometry, material)
cube.castShadow = true // 投射阴影
scene.add(cube)
// 5. 创建地面
const groundGeometry = new THREE.PlaneGeometry(10, 10)
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x666666 })
const ground = new THREE.Mesh(groundGeometry, groundMaterial)
ground.rotation.x = -Math.PI / 2
ground.position.y = -1
ground.receiveShadow = true // 接收阴影
scene.add(ground)
// 6. 创建光源
const ambientLight = new THREE.AmbientLight(0x404040, 0.5)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 5, 5)
directionalLight.castShadow = true
scene.add(directionalLight)
// 7. 添加控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 开启阻尼效果
controls.dampingFactor = 0.05
// 8. 渲染循环
function animate() {
requestAnimationFrame(animate)
// 旋转立方体
cube.rotation.x += 0.01
cube.rotation.y += 0.01
// 更新控制器
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)
})
</script>
</body>
</html>代码解析
让我们逐步分析这段代码的每个部分:
1. 导入 Three.js
javascript
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js'
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/controls/OrbitControls.js'- 第一行导入 Three.js 核心库
- 第二行导入 OrbitControls 控制器(用于鼠标交互)
模块化导入方式对比:
| 方式 | 说明 | 推荐场景 |
|---|---|---|
import * as THREE from 'three' | 全局导入,包含所有功能 | 快速原型开发 |
import { Scene, Camera } from 'three' | 按需导入 | 生产项目,减小体积 |
| CDN 导入 | 无需构建工具 | 学习、演示 |
2. 创建场景(Scene)
javascript
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x333333)场景是所有 3D 对象的容器,我们设置了深灰色的背景。
Scene 常用属性:
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
background | Color/Texture | 背景色或贴图 | scene.background = new THREE.Color(0x333333) |
environment | Texture | 环境贴图 | scene.environment = envTexture |
fog | Fog/FogExp2 | 雾效果 | scene.fog = new THREE.Fog(0x000000, 1, 100) |
javascript
// 添加雾效果示例
scene.fog = new THREE.Fog(0x333333, 1, 20) // 线性雾
// 或
scene.fog = new THREE.FogExp2(0x333333, 0.1) // 指数雾3. 创建相机(Camera)
javascript
const camera = new THREE.PerspectiveCamera(
75, // 视野角度(FOV)
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
)
camera.position.set(0, 0, 5)参数说明:
- 视野角度(FOV):75 度,决定可视范围。常用值:45-90
- 宽高比:窗口宽度/高度,必须与画布比例一致
- 近裁剪面:距离相机 0.1 单位以内的对象不会被渲染
- 远裁剪面:距离相机 1000 单位以外的对象不会被渲染
视锥体示意图:
plaintext
Far Plane (远裁剪面)
╱────────╲
╱ ╲
╱ ╲
╱ 可视区域 ╲
╱ ╲
╱ ╲
╱────────────────────╲
Near Plane (近裁剪面)
相机位置 (Camera)相机位置设置:
javascript
// 方式1:使用 set 方法
camera.position.set(x, y, z)
// 方式2:直接设置属性
camera.position.x = 0
camera.position.y = 0
camera.position.z = 5
// 设置相机朝向
camera.lookAt(0, 0, 0) // 看向原点
// 或
camera.lookAt(new THREE.Vector3(0, 0, 0))4. 创建渲染器(Renderer)
javascript
const renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
alpha: true // 透明背景
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.shadowMap.enabled = true
document.body.appendChild(renderer.domElement)antialias:开启抗锯齿,让边缘更平滑(性能消耗约 5-10%)alpha:支持透明背景,便于叠加到其他元素上setSize:设置渲染器大小,应与 canvas 尺寸一致setPixelRatio:设置像素比,适配高清屏(建议限制最大值)shadowMap.enabled:开启阴影渲染
渲染器配置最佳实践:
javascript
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
powerPreference: 'high-performance' // 高性能模式
})
// 限制像素比,避免高清屏性能问题
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// 设置阴影类型
renderer.shadowMap.type = THREE.PCFSoftShadowMap // 柔和阴影
// 启用物理正确的光照
renderer.physicallyCorrectLights = true
// 色彩管理
renderer.outputColorSpace = THREE.SRGBColorSpace
// 色调映射(HDR 效果)
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 1.0渲染信息查看:
javascript
// 查看渲染统计信息
console.log(renderer.info)
// {
// memory: { geometries: 1, textures: 1 },
// render: { calls: 2, triangles: 36, points: 0, lines: 0 }
// }
// 在渲染循环中监控
function animate() {
console.log('Draw Calls:', renderer.info.render.calls)
console.log('Triangles:', renderer.info.render.triangles)
// ...
}5. 创建几何体和材质
javascript
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
})
const cube = new THREE.Mesh(geometry, material)- 几何体(Geometry):定义形状(这里是 1x1x1 的立方体)
- 材质(Material):定义外观(绿色,半金属半粗糙)
- 网格(Mesh):几何体 + 材质的组合
常用几何体参数:
| 几何体 | 参数 | 说明 |
|---|---|---|
BoxGeometry | width, height, depth | 立方体尺寸 |
SphereGeometry | radius, widthSegments, heightSegments | 半径和分段数 |
CylinderGeometry | radiusTop, radiusBottom, height | 圆柱体 |
PlaneGeometry | width, height | 平面尺寸 |
材质类型选择指南:
| 材质 | 特点 | 适用场景 |
|---|---|---|
MeshBasicMaterial | 不受光照影响,性能最好 | 简单物体、UI元素 |
MeshStandardMaterial | PBR材质,效果真实 | 大多数3D场景 |
MeshPhongMaterial | Phong光照模型 | 需要高光的物体 |
MeshLambertMaterial | Lambert光照模型 | 漫反射物体 |
javascript
// MeshStandardMaterial PBR 材质详解
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00, // 基础颜色
metalness: 0.5, // 金属度 (0-1),1=完全金属
roughness: 0.5, // 粗糙度 (0-1),0=光滑镜面
envMapIntensity: 1.0, // 环境贴图强度
// 可选贴图
map: colorMap, // 颜色贴图
normalMap: normalMap, // 法线贴图
roughnessMap: roughMap, // 粗糙度贴图
metalnessMap: metalMap, // 金属度贴图
aoMap: aoMap, // 环境遮蔽贴图
emissiveMap: emissiveMap, // 自发光贴图
emissive: 0x000000, // 自发光颜色
emissiveIntensity: 1 // 自发光强度
})6. 创建光源
javascript
const ambientLight = new THREE.AmbientLight(0x404040, 0.5)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)- 环境光(AmbientLight):均匀照亮场景中所有对象,无方向
- 方向光(DirectionalLight):模拟太阳光,产生阴影
光源类型详解:
| 光源类型 | 说明 | 阴影 | 性能 | 典型用途 |
|---|---|---|---|---|
AmbientLight | 环境光 | 无 | 低 | 基础照明 |
DirectionalLight | 平行光 | 有 | 中 | 太阳光 |
PointLight | 点光源 | 有 | 中 | 灯泡、火光 |
SpotLight | 聚光灯 | 有 | 高 | 手电筒、舞台灯 |
HemisphereLight | 半球光 | 无 | 低 | 天空+地面照明 |
javascript
// 创建多种光源示例
// 环境光 - 基础照明
const ambient = new THREE.AmbientLight(0x404040, 0.5)
scene.add(ambient)
// 方向光 - 主光源
const directional = new THREE.DirectionalLight(0xffffff, 1)
directional.position.set(5, 10, 5)
directional.castShadow = true
// 配置阴影
directional.shadow.mapSize.width = 2048
directional.shadow.mapSize.height = 2048
directional.shadow.camera.near = 0.5
directional.shadow.camera.far = 50
scene.add(directional)
// 点光源 - 辅助光源
const point = new THREE.PointLight(0xff0000, 1, 10)
point.position.set(0, 2, 0)
scene.add(point)
// 半球光 - 天空地面照明
const hemisphere = new THREE.HemisphereLight(0x87CEEB, 0x8B4513, 0.5)
scene.add(hemisphere)7. 添加控制器
javascript
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 开启阻尼效果
controls.dampingFactor = 0.05OrbitControls 配置详解:
javascript
const controls = new OrbitControls(camera, renderer.domElement)
// 基础配置
controls.enableDamping = true // 开启阻尼(惯性)
controls.dampingFactor = 0.05 // 阻尼系数
controls.autoRotate = false // 自动旋转
controls.autoRotateSpeed = 2.0 // 自动旋转速度
// 限制范围
controls.minDistance = 1 // 最小缩放距离
controls.maxDistance = 100 // 最大缩放距离
controls.minPolarAngle = 0 // 垂直旋转最小角度
controls.maxPolarAngle = Math.PI // 垂直旋转最大角度
// 禁用/启用交互
controls.enableZoom = true // 启用缩放
controls.enableRotate = true // 启用旋转
controls.enablePan = true // 启用平移
// 平滑缩放
controls.zoomSpeed = 1.0
controls.rotateSpeed = 1.0
controls.panSpeed = 1.0
// 设置目标点
controls.target.set(0, 0, 0)
controls.update() // 更新控制器其他控制器类型:
| 控制器 | 说明 | 适用场景 |
|---|---|---|
OrbitControls | 轨道控制器 | 通用3D预览 |
FlyControls | 飞行控制器 | 飞行模拟 |
FirstPersonControls | 第一人称 | FPS游戏 |
PointerLockControls | 指针锁定 | 沉浸式体验 |
TrackballControls | 轨迹球 | 自由旋转 |
8. 渲染循环
javascript
function animate() {
requestAnimationFrame(animate)
// 旋转立方体
cube.rotation.x += 0.01
cube.rotation.y += 0.01
// 更新控制器
controls.update()
// 渲染场景
renderer.render(scene, camera)
}
animate()渲染循环是 Three.js 的核心:
- requestAnimationFrame:浏览器优化的定时器,与屏幕刷新率同步(通常 60fps)
- 更新对象状态:修改位置、旋转、缩放等
- 更新控制器:如果有阻尼效果,需要每帧更新
- 渲染场景:将3D场景投影到2D画布
渲染循环优化:
javascript
// 使用 Clock 获取准确的时间增量
const clock = new THREE.Clock()
function animate() {
requestAnimationFrame(animate)
const deltaTime = clock.getDelta() // 两帧之间的时间差(秒)
const elapsedTime = clock.getElapsedTime() // 总运行时间
// 使用 deltaTime 实现帧率无关的动画
cube.rotation.x += deltaTime
cube.rotation.y += deltaTime * 0.5
controls.update()
renderer.render(scene, camera)
}按需渲染(非连续动画场景):
javascript
let needsUpdate = true
function render() {
if (!needsUpdate) return
renderer.render(scene, camera)
needsUpdate = false
}
// 只在需要时更新
controls.addEventListener('change', () => {
needsUpdate = true
render()
})
// 初始渲染
render()构建工具项目
对于实际项目,推荐使用构建工具(如 Vite)。以下是完整的项目搭建步骤:
1. 创建项目
bash
# 创建项目目录
mkdir threejs-demo
cd threejs-demo
# 初始化项目
npm init -y
# 安装依赖
npm install three
npm install -D vite2. 项目结构
plaintext
threejs-demo/
├── index.html
├── src/
│ ├── main.js
│ └── style.css
├── package.json
└── vite.config.js3. 配置文件
vite.config.js
javascript
import { defineConfig } from 'vite'
export default defineConfig({
server: {
port: 3000,
open: true
}
})package.json
json
{
"name": "threejs-demo",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"three": "^0.150.0"
},
"devDependencies": {
"vite": "^4.0.0"
}
}4. HTML 文件
index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Demo</title>
<link rel="stylesheet" href="/src/style.css">
</head>
<body>
<script type="module" src="/src/main.js"></script>
</body>
</html>5. 样式文件
src/style.css
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
background-color: #000;
}
canvas {
display: block;
}6. JavaScript 文件
src/main.js
javascript
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
class ThreeScene {
constructor() {
this.scene = null
this.camera = null
this.renderer = null
this.cube = null
this.controls = null
this.init()
}
init() {
// 创建场景
this.createScene()
// 创建相机
this.createCamera()
// 创建渲染器
this.createRenderer()
// 创建对象
this.createObjects()
// 创建光源
this.createLights()
// 创建控制器
this.createControls()
// 添加事件监听
this.addEventListeners()
// 开始渲染循环
this.animate()
}
createScene() {
this.scene = new THREE.Scene()
this.scene.background = new THREE.Color(0x333333)
}
createCamera() {
const aspect = window.innerWidth / window.innerHeight
this.camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000)
this.camera.position.set(0, 0, 5)
}
createRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.renderer.setSize(window.innerWidth, window.innerHeight)
this.renderer.setPixelRatio(window.devicePixelRatio)
this.renderer.shadowMap.enabled = true
document.body.appendChild(this.renderer.domElement)
}
createObjects() {
// 创建立方体
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
})
this.cube = new THREE.Mesh(geometry, material)
this.cube.castShadow = true
this.scene.add(this.cube)
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(10, 10)
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x666666 })
const ground = new THREE.Mesh(groundGeometry, groundMaterial)
ground.rotation.x = -Math.PI / 2
ground.position.y = -1
ground.receiveShadow = true
this.scene.add(ground)
}
createLights() {
// 环境光
const ambientLight = new THREE.AmbientLight(0x404040, 0.5)
this.scene.add(ambientLight)
// 方向光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 5, 5)
directionalLight.castShadow = true
this.scene.add(directionalLight)
}
createControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
this.controls.enableDamping = true
this.controls.dampingFactor = 0.05
}
addEventListeners() {
window.addEventListener('resize', () => this.onWindowResize())
}
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(window.innerWidth, window.innerHeight)
}
animate() {
requestAnimationFrame(() => this.animate())
// 旋转立方体
this.cube.rotation.x += 0.01
this.cube.rotation.y += 0.01
// 更新控制器
this.controls.update()
// 渲染场景
this.renderer.render(this.scene, this.camera)
}
}
// 创建场景实例
new ThreeScene()7. 运行项目
bash
npm run dev浏览器会自动打开 http://localhost:3000,看到旋转的立方体。
添加更多对象
让我们扩展场景,添加更多 3D 对象:
javascript
createObjects() {
// 立方体
const boxGeometry = new THREE.BoxGeometry(1, 1, 1)
const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 })
const box = new THREE.Mesh(boxGeometry, boxMaterial)
box.position.set(-2, 0, 0)
box.castShadow = true
this.scene.add(box)
// 球体
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32)
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 })
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
sphere.position.set(0, 0, 0)
sphere.castShadow = true
this.scene.add(sphere)
// 圆柱体
const cylinderGeometry = new THREE.CylinderGeometry(0.3, 0.3, 1, 32)
const cylinderMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff })
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial)
cylinder.position.set(2, 0, 0)
cylinder.castShadow = true
this.scene.add(cylinder)
// 圆环
const torusGeometry = new THREE.TorusGeometry(0.5, 0.2, 16, 100)
const torusMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 })
const torus = new THREE.Mesh(torusGeometry, torusMaterial)
torus.position.set(0, 2, 0)
torus.castShadow = true
this.scene.add(torus)
// 保存对象引用
this.objects = { box, sphere, cylinder, torus }
}
animate() {
requestAnimationFrame(() => this.animate())
const time = Date.now() * 0.001
// 旋转所有对象
Object.values(this.objects).forEach((obj, index) => {
obj.rotation.x += 0.01
obj.rotation.y += 0.01
obj.position.y = Math.sin(time + index) * 0.5
})
this.controls.update()
this.renderer.render(this.scene, this.camera)
}性能监控
添加性能监控工具:
javascript
import Stats from 'three/examples/jsm/libs/stats.module.js'
class ThreeScene {
constructor() {
this.stats = null
// ...
}
init() {
// ...
this.createStats()
// ...
}
createStats() {
this.stats = new Stats()
this.stats.showPanel(0) // 0: fps, 1: ms, 2: mb
document.body.appendChild(this.stats.dom)
}
animate() {
this.stats.begin()
requestAnimationFrame(() => this.animate())
// ...动画代码
this.stats.end()
this.renderer.render(this.scene, this.camera)
}
}常见问题
1. 场景是空白的
原因:
- 相机位置不正确
- 没有调用 render()
- 对象在视锥体之外
解决方案:
javascript
// 检查相机位置
console.log(camera.position)
// 检查对象位置
scene.traverse((obj) => {
console.log(obj.type, obj.position)
})
// 确保调用了 render()
renderer.render(scene, camera)
// 检查对象是否在视锥体内
const frustum = new THREE.Frustum()
const matrix = new THREE.Matrix4().multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
)
frustum.setFromProjectionMatrix(matrix)
if (frustum.intersectsObject(mesh)) {
console.log('对象在视锥体内')
} else {
console.log('对象在视锥体外,调整相机位置')
}2. 对象是黑色的
原因:
- 没有光源
- 使用了需要光照的材质(如 MeshStandardMaterial)
- 法线方向错误
解决方案:
javascript
// 添加光源
const light = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(light)
// 或使用不需要光照的材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 检查并修复法线
geometry.computeVertexNormals()
// 翻转法线(如果需要)
geometry.scale(-1, 1, 1)3. 窗口大小变化时画面变形
原因:没有更新相机的宽高比
解决方案:
javascript
window.addEventListener('resize', () => {
// 获取新尺寸
const width = window.innerWidth
const height = window.innerHeight
// 更新相机
camera.aspect = width / height
camera.updateProjectionMatrix()
// 更新渲染器
renderer.setSize(width, height)
})
// 如果渲染到容器内(而非全屏)
const container = document.getElementById('container')
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(width, height)
}
})
resizeObserver.observe(container)4. 性能问题
解决方案:
javascript
// 1. 减少多边形数量
const geometry = new THREE.BoxGeometry(1, 1, 1, 4, 4, 4) // 较少分段
// 2. 使用低分辨率
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// 3. 关闭抗锯齿(如果不需要)
const renderer = new THREE.WebGLRenderer({ antialias: false })
// 4. 合并几何体
import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils'
const mergedGeometry = BufferGeometryUtils.mergeGeometries([geo1, geo2, geo3])
// 5. 使用 InstancedMesh(大量相同对象)
const count = 1000
const mesh = new THREE.InstancedMesh(geometry, material, count)
const matrix = new THREE.Matrix4()
for (let i = 0; i < count; i++) {
matrix.setPosition(Math.random() * 10, Math.random() * 10, Math.random() * 10)
mesh.setMatrixAt(i, matrix)
}
// 6. 使用 LOD(细节层次)
const lod = new THREE.LOD()
lod.addLevel(highPolyMesh, 0) // 0米内显示高模
lod.addLevel(midPolyMesh, 10) // 10米内显示中模
lod.addLevel(lowPolyMesh, 20) // 20米内显示低模5. 阴影显示异常
原因:
- 阴影贴图分辨率太低
- 光源范围设置不当
- 未开启对象阴影
解决方案:
javascript
// 1. 提高阴影贴图分辨率
renderer.shadowMap.enabled = true
directionalLight.shadow.mapSize.width = 2048
directionalLight.shadow.mapSize.height = 2048
// 2. 调整光源阴影相机范围
directionalLight.shadow.camera.near = 0.1
directionalLight.shadow.camera.far = 100
directionalLight.shadow.camera.left = -20
directionalLight.shadow.camera.right = 20
directionalLight.shadow.camera.top = 20
directionalLight.shadow.camera.bottom = -20
// 3. 确保对象开启了阴影
mesh.castShadow = true // 投射阴影
ground.receiveShadow = true // 接收阴影
// 4. 使用柔和阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap6. 纹理显示模糊
解决方案:
javascript
// 1. 检查纹理尺寸(建议2的幂次方)
const texture = textureLoader.load('texture.jpg')
// 1024x1024, 512x512, 256x256...
// 2. 设置正确的纹理过滤
texture.minFilter = THREE.LinearMipmapLinearFilter
texture.magFilter = THREE.LinearFilter
// 3. 生成 mipmaps
texture.generateMipmaps = true
// 4. 设置各向异性过滤
const maxAnisotropy = renderer.capabilities.getMaxAnisotropy()
texture.anisotropy = maxAnisotropy7. 点击交互无响应
解决方案:
javascript
// 确保正确设置射线检测
import { Raycaster, Vector2 } from 'three'
const raycaster = new Raycaster()
const mouse = new Vector2()
// 监听点击事件
canvas.addEventListener('click', (event) => {
// 计算鼠标位置(归一化设备坐标)
const rect = canvas.getBoundingClientRect()
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
// 设置射线
raycaster.setFromCamera(mouse, camera)
// 检测相交对象
const intersects = raycaster.intersectObjects(scene.children, true)
if (intersects.length > 0) {
console.log('点击了:', intersects[0].object)
}
})下一步
恭喜你完成了第一个 Three.js 场景!接下来你可以: