整理了两套 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/') 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) } })
|
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. 官方文档直达
用法:文档页右上角可切换语言和版本(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
| 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
|
主题 2:工业/管廊 3D
| 效果 |
实现思路 |
复用难度 |
| 模型加载 |
GLTF + Draco 压缩 |
易 |
| 点击拾取 + 描边 |
射线 + OutlinePass |
中 |
| 流光管 |
管线几何体 + Shader 让光沿管道流动 |
中 |
| 材质分类高亮 |
遍历模型按 name/type 改材质颜色 |
易 |
| 相机漫游 |
沿预设路径缓动相机位置 |
中 |
| HDR 环境 |
RGBELoader 加载 .hdr 做环境光 |
易 |
流光管 Shader(沿 UV.x 方向流动):
1 2 3 4
| 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
| 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() }
|
四、踩坑记录(项目实测)
- 模型发灰:忘设
renderer.outputColorSpace = SRGBColorSpace,纹理也检查 texture.colorSpace
- Draco 报错:解码器路径必须指向本地
draco/ 目录,不能只装包
- 后处理与普通渲染共存:用了 composer 后,每帧要
composer.render() 而不是 renderer.render(),且 resize 要同步更新 composer 尺寸
- 描边抖动:OutlinePass 依赖深度缓冲,必要时开
logarithmicDepthBuffer
- 内存泄漏:切场景时旧模型要
traverse 里 geometry.dispose()、material.dispose(),否则 GPU 内存持续上涨
- 射线拾取大模型:
intersectObjects(children, true) 要传 true 递归,否则只点到外层
- 物理引擎坐标:cannon-es 和 three 单位一致(米),但碰撞体
position 要同步更新,否则物体穿模
五、总结
- 3D 效果 = 几何 + 材质 + 动画,所有炫酷效果最后都落到这三件事
- Shader 是分水岭:会改 Shader 才能做流光、雷达、海面这些”非标准”效果
- 复用优先:飞线、雷达、光墙、描边、拾取,这些是项目里反复出现的”零件”,封装成函数,新项目直接调用
- 性能是底线:模型数量一多,先合并 draw call、再减面、再压缩纹理
- 源码就是最好的 API 文档:r179 的
examples/jsm 每个模块都有官方示例,看不懂的 API 直接抄官方示例改
内容由 AI 生成,仅供参考。本文发布于 码上学习,转载请注明出处。