Three.js 学习笔记与可复用效果速查

整理了两套 Vue3 + Three.js 项目的完整源码,结合 three.js r179 官方 API,提炼出速查模板、API 速查和可直接复用的展示效果。写 3D 需求时直接抄。

一、速查模板(直接抄)

1. 标准场景启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 2000)
camera.position.set(50, 50, 50)

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(innerWidth, innerHeight)
// 关键:颜色空间,不改会"发灰"
renderer.outputColorSpace = THREE.SRGBColorSpace
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 缓动
controls.minDistance = 10
controls.maxDistance = 500

// 动画循环
const clock = new THREE.Clock()
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
animate()

// 窗口自适应
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight
camera.updateProjectionMatrix()
renderer.setSize(innerWidth, innerHeight)
})

2. 灯光三件套

1
2
3
4
5
6
7
8
9
10
// 环境光:打底,否则黑黢黢
scene.add(new THREE.AmbientLight(0xffffff, 0.5))
// 平行光:模拟太阳,产生明暗
const sun = new THREE.DirectionalLight(0xffffff, 1)
sun.position.set(100, 100, 100)
scene.add(sun)
// 点光:局部补光
const point = new THREE.PointLight(0xff0000, 2, 100)
point.position.set(0, 20, 0)
scene.add(point)

3. GLTF 模型加载(含 Draco 压缩)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'

const draco = new DRACOLoader()
draco.setDecoderPath('/draco/') // 本地放 draco 解码器
const loader = new GLTFLoader()
loader.setDRACOLoader(draco)

loader.load('/models/pipe.glb', (gltf) => {
const model = gltf.scene
scene.add(model)
// 遍历材质统一处理
model.traverse((child) => {
if (child.isMesh) {
child.material.metalness = 0.3
child.material.roughness = 0.6
}
})
})

4. 射线拾取(点击选中)

1
2
3
4
5
6
7
8
9
10
11
12
13
const raycaster = new THREE.Raycaster()
const pointer = new THREE.Vector2()

renderer.domElement.addEventListener('click', (e) => {
pointer.x = (e.clientX / innerWidth) * 2 - 1
pointer.y = -(e.clientY / innerHeight) * 2 + 1
raycaster.setFromCamera(pointer, camera)
const hits = raycaster.intersectObjects(scene.children, true)
if (hits.length) {
const obj = hits[0].object
console.log('点击了', obj.name) // 配合自定义 name/type 做业务
}
})

5. 描边高亮(OutlinePass)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'

const composer = new EffectComposer(renderer)
composer.addPass(new RenderPass(scene, camera))

const outline = new OutlinePass(new THREE.Vector2(innerWidth, innerHeight), scene, camera)
outline.edgeStrength = 5
outline.pulseSpeed = 2
composer.addPass(outline)

// 点击后高亮:
outline.selectedObjects = [clickedMesh]

6. 工具类封装(项目里通用)

1
2
3
4
5
6
7
8
// 生成随机盒子(智慧城市建筑群)
export function createRandomBox(size = 10) {
const geo = new THREE.BoxGeometry(size, size * (0.5 + Math.random() * 2), size)
const mat = new THREE.MeshStandardMaterial({
color: new THREE.Color().setHSL(0.55 + Math.random() * 0.1, 1, 0.4),
})
return new THREE.Mesh(geo, mat)
}

二、API 速查(three.js r179 examples/jsm)

1. 官方文档直达

入口 地址 说明
官方文档主页 threejs.org/docs 完整 API 索引,右上角可切换中文
中文手册 docs#manual/zh 简体中文,从”创建场景”入门
官方示例 threejs.org/examples 所有模块的在线可运行 demo,抄代码首选
编辑器 threejs.org/editor 在线可视化编辑器,拖拽搭场景导出
源码仓库 github.com/mrdoob/three.js 想查实现细节直接搜源码

用法:文档页右上角可切换语言和版本(r179 对应 0.179.x);示例页每个 demo 下方有”view source”,实现细节全在源码里。

2. 核心类速查(从 three 导入)

分类 类 用途
场景 Scene 所有对象的容器
相机 PerspectiveCamera / OrthographicCamera 透视投影 / 正交投影
渲染器 WebGLRenderer 主渲染器,{ antialias, alpha } 可配
几何体 BoxGeometry、SphereGeometry、PlaneGeometry、CylinderGeometry、TubeGeometry 常用几何体
曲线 CatmullRomCurve3、QuadraticBezierCurve3 飞线/管道路径
材质 MeshBasicMaterial(不受光)、MeshStandardMaterial(PBR)、MeshPhongMaterial、ShaderMaterial(自定义着色器) 材质族
灯光 AmbientLight、DirectionalLight、PointLight、SpotLight、HemisphereLight 灯光族
纹理 TextureLoader、CanvasTexture(程序生成图)、CubeTextureLoader 贴图加载
动画 AnimationMixer(模型骨骼动画)、Clock(时间差) 动画
数学 Vector3、Quaternion、Matrix4、Color、Raycaster(拾取) 数学工具
对象 Mesh、Group、Sprite(广告牌)、InstancedMesh(性能合并)、LOD 对象类型

3. examples/jsm 模块速查(按目录)

目录 模块 用途
controls OrbitControls 轨道控制(旋转/缩放/平移),最常用
controls TransformControls 移动/旋转/缩放 gizmo(场景编辑器)
controls DragControls 拖拽物体
controls PointerLockControls FPS 鼠标锁定(第一人称)
controls FirstPersonControls / FlyControls 第一人称 / 自由飞行
loaders GLTFLoader gltf/glb 模型
loaders DRACOLoader Draco 压缩模型解码
loaders FBXLoader / OBJLoader / PLYLoader 其他格式模型
loaders RGBELoader / EXRLoader HDR 环境贴图
loaders FontLoader + TextGeometry 3D 文字
loaders KTX2Loader GPU 压缩纹理
postprocessing EffectComposer 后处理入口(所有后处理都要挂它)
postprocessing RenderPass 基础渲染通道
postprocessing OutlinePass 描边高亮
postprocessing UnrealBloomPass 泛光发光
postprocessing SMAAPass(抗锯齿)/ SSAOPass(环境遮蔽)/ SSRPass(反射) 画质增强
postprocessing ShaderPass 自定义着色器通道
objects Sky 程序化天空
objects Water 程序化水面
objects Reflector / Refractor 镜面反射 / 折射
objects LightningStruck 闪电
renderers CSS2DRenderer / CSS3DRenderer HTML 标签 / 3D DOM 元素(如标注)
misc GPUComputationRenderer GPU 计算(粒子系统)
utils BufferGeometryUtils 几何体合并/转换
utils SkeletonUtils 骨骼动画克隆/重定向
helpers AxesHelper、GridHelper、BoxHelper、VertexNormalsHelper 调试辅助
libs lil-gui(调试面板)、tween(补间动画)、fflate(压缩解压) 第三方工具

4. 面试/项目常考能力点

  • 渲染管线:顶点着色器 → 光栅化 → 片元着色器
  • 颜色空间:SRGBColorSpace 不改就灰蒙蒙
  • 性能:draw call 合并(InstancedMesh)、模型精简、纹理压缩、dispose 释放
  • 坐标系:Three 右手系,Z 轴向屏幕外
  • 事件拾取:射线 → 相交 → 业务对象

三、可复用展示效果(按主题速查)

主题 1:智慧城市大屏

效果 实现思路 复用难度
飞线动画 线/管几何体 + 纹理 offset 随时间平移 易
雷达扫描 平面圆环 + Shader 内从中心扩散的圆环波 中
光墙 圆柱体 + Shader 边缘发光、透明度衰减 中
告警精灵 在告警点生成 Sprite,canvas 画圆点做纹理 易
建筑楼群 大量随机盒子 + 夜景材质(自发光) 易
事件联动 大屏左侧点击 → mitt 发事件 → 3D 场景响应 易

飞线核心代码(Shader 或纹理偏移):

1
2
3
4
5
6
7
8
// 用管线 + 纹理流动实现流光效果
const curve = new THREE.CatmullRomCurve3([p1, p2, p3]) // 贝塞尔式曲线
const geo = new THREE.TubeGeometry(curve, 64, 0.2, 8, false)
const mat = new THREE.MeshBasicMaterial({
map: flowTexture, // 横向渐变条纹贴图
})
// 动画:纹理偏移模拟流动
mat.map.offset.x -= 0.05 * delta

雷达 Shader(核心片段):

1
2
3
4
5
// fragment
float dist = distance(vUv, vec2(0.5)); // 到中心距离
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
float wave = sin(dist * 40.0 - uTime * 5.0); // 扩散波
gl_FragColor = vec4(uColor, alpha * step(0.5, wave));

夜景材质(项目 B 的 modifyCityMaterial):

1
2
3
4
// 白天模型变夜景:屋顶自发光 + 开窗亮灯效果
mat.emissive.setHSL(0.55, 1, 0.3)
mat.emissiveIntensity = 0.8
// 通过 uTime 让楼体随机闪烁

主题 2:工业/管廊 3D

效果 实现思路 复用难度
模型加载 GLTF + Draco 压缩 易
点击拾取 + 描边 射线 + OutlinePass 中
流光管 管线几何体 + Shader 让光沿管道流动 中
材质分类高亮 遍历模型按 name/type 改材质颜色 易
相机漫游 沿预设路径缓动相机位置 中
HDR 环境 RGBELoader 加载 .hdr 做环境光 易

流光管 Shader(沿 UV.x 方向流动):

1
2
3
4
// vertex 里把 uv.x 传下去,fragment 里做渐变 + 时间偏移
float flow = fract(vUv.x * 3.0 - uTime * 0.5);
float glow = smoothstep(0.9, 1.0, flow);
gl_FragColor = vec4(uColor, 1.0) * glow;

主题 3:汽车展厅

效果 实现思路 复用难度
车型选择 多 GLTF 切换 + 过渡动画 易
颜色切换 material.color.set() + 车漆金属感参数 易
tween 动画 选色时用 tween 让车身颜色渐变 易
车灯点亮 点光源 + 灯罩自发光材质开关 易
自动旋转 相机绕目标圆周运动 + 阻尼 易
1
2
3
4
5
6
7
8
// 颜色渐变换色(tween 或手工 lerp)
const targetColor = new THREE.Color('#ff0000')
const tween = { t: 0 }
function tick() {
tween.t = Math.min(1, tween.t + 0.02)
paintMaterial.color.lerp(targetColor, tween.t)
requestAnimationFrame(tick)
}

主题 4:海景大场景(进阶)

效果 实现思路 复用难度
海面 大平面 + Shader 顶点波浪位移 高
云层 多个半透明平面错层 + 缓慢位移 中
昼夜切换 gsap 改场景背景色 + 灯光强度 中
镜面反射 Reflector 组件 高
物理引擎 cannon-es 模拟碰撞/浮力 高
后处理 EffectComposer + UnrealBloom(发光)/SSR(反射)/SMAA(抗锯齿) 高
1
2
3
4
5
6
7
8
9
// 后处理发光管线
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
const bloom = new UnrealBloomPass(
new THREE.Vector2(innerWidth, innerHeight), // 分辨率
0.8, // 强度
0.4, // 半径
0.85 // 阈值
)
composer.addPass(bloom)

主题 5:第一人称/元宇宙

效果 实现思路 复用难度
第一人称控制 PointerLockControls 鼠标锁 + WASD 中
碰撞检测 Octree 动态碰撞体 高
动作切换 AnimationMixer + fadeToAction 平滑过渡 中
LOD 细节 远近切换高低模 中
双相机 第一人称/俯瞰切换 中
1
2
3
4
5
6
7
8
9
10
// 动画动作平滑切换
import { AnimationMixer } from 'three'
const mixer = new AnimationMixer(model)
const actions = Object.fromEntries(
anims.map((clip) => [clip.name, mixer.clipAction(clip)])
)
function fadeTo(name) {
Object.values(actions).forEach((a) => a.fadeOut(0.3))
actions[name].reset().fadeIn(0.3).play()
}

四、踩坑记录(项目实测)

  1. 模型发灰:忘设 renderer.outputColorSpace = SRGBColorSpace,纹理也检查 texture.colorSpace
  2. Draco 报错:解码器路径必须指向本地 draco/ 目录,不能只装包
  3. 后处理与普通渲染共存:用了 composer 后,每帧要 composer.render() 而不是 renderer.render(),且 resize 要同步更新 composer 尺寸
  4. 描边抖动:OutlinePass 依赖深度缓冲,必要时开 logarithmicDepthBuffer
  5. 内存泄漏:切场景时旧模型要 traverse 里 geometry.dispose()、material.dispose(),否则 GPU 内存持续上涨
  6. 射线拾取大模型:intersectObjects(children, true) 要传 true 递归,否则只点到外层
  7. 物理引擎坐标:cannon-es 和 three 单位一致(米),但碰撞体 position 要同步更新,否则物体穿模

五、总结

  1. 3D 效果 = 几何 + 材质 + 动画,所有炫酷效果最后都落到这三件事
  2. Shader 是分水岭:会改 Shader 才能做流光、雷达、海面这些”非标准”效果
  3. 复用优先:飞线、雷达、光墙、描边、拾取,这些是项目里反复出现的”零件”,封装成函数,新项目直接调用
  4. 性能是底线:模型数量一多,先合并 draw call、再减面、再压缩纹理
  5. 源码就是最好的 API 文档:r179 的 examples/jsm 每个模块都有官方示例,看不懂的 API 直接抄官方示例改

内容由 AI 生成,仅供参考。本文发布于 码上学习,转载请注明出处。