THREE+VUE3+VITE THREE.JS基础教学
从零入门 Three.js:基于 Vue 组件封装的基础教学
适合刚接触 Three.js 的同学阅读。本文不是只讲一个孤立 Demo,而是结合项目中的实际页面(如 src/components/three/测试封装.vue),带你理解 Three.js 的基础组成、封装思路和常见交互。学完后,你就能自己搭建基础 3D 场景并集成到 Vue 项目中。
一、Three.js 是什么
Three.js 是一个基于 WebGL 的 3D 渲染库,它屏蔽了底层图形接口的复杂性,让我们用更简单的方式在网页中创建 3D 场景、模型、灯光和动画。对于初学者,记住这句话:
Three.js 的核心流程是
场景 Scene + 相机 Camera + 渲染器 Renderer + 几何体 Geometry + 材质 Material。
这几个对象组合起来,就能生成最基础的 3D 画面。例如,你想在页面上显示一个立方体,就得创建场景、添加物体、设置相机视角,并由渲染器画出结果。
二、一个 Three.js 页面最少要有哪些东西
结合项目结构,Three.js 的基础流程可以总结为 5 步:
- 创建场景
Scene:3D 世界的“舞台”。 - 创建几何体
Geometry:决定物体的形状。 - 创建材质
Material:定义物体的外观。 - 创建网格模型
Mesh:几何体和材质的组合,代表真实的物体。 - 创建相机和渲染器:设置观看角度并渲染到 DOM 容器。
在项目中,这些被拆分到:
- 页面组件:处理 Vue 生命周期(挂载和销毁)。
src/unit/three/几何体BufferGeometry.ts:初始化 Three.js 场景。src/unit/three/guiJs.ts:处理 GUI 交互。
这样的封装方式便于教学和扩展,阅读代码时重点关注初始化和销毁逻辑。
三、先看页面入口:Vue 组件怎么接住 Three.js
页面组件(如 src/components/three/three.vue)的核心职责:获取容器 DOM,在组件挂载时初始化 Three.js,卸载时销毁资源,防止内存泄漏。核心结构如下:
<template>
<div class="neirong">
<div class="three-box" ref="threeJSDom" style="width: 100%; height: 100%;"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import { initThree } from '@/unit/three/thress'
import { createGui } from '@/unit/three/guiJs'
const threeJSDom = ref<HTMLDivElement | null>(null)
let threeApi: ReturnType<typeof initThree> | null = null
let guiApi: ReturnType<typeof createGui> | null = null
onMounted(() => {
if (!threeJSDom.value) return
threeApi = initThree(threeJSDom.value)
guiApi = createGui({
dat: window.dat,
material: threeApi.material,
mesh: threeApi.mesh,
scene: threeApi.scene,
camera: threeApi.camera,
renderer: threeApi.renderer,
render: threeApi.render,
})
})
onUnmounted(() => {
guiApi?.destroy()
threeApi?.destroy()
})
</script>
关键点:Three.js 不是直接写在 template 里,而是挂载到容器 DOM 上(如 threeJSDom),由 Vue 生命周期管理初始化和销毁。这比堆代码更清晰、易维护。
文件2 thress.ts 该文件介绍了如何生成场景,几何体,材质,相机 等等
import *as THREE from 'three'
// 引入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export function initThree(container: HTMLElement) {
// 1.创建一个3D场景对象scene
const scene = new THREE.Scene()
// 2.创建形状-长方形
const geometry = new THREE.BoxGeometry(100, 100, 100)
// 3.设置材质——网格高光材质
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,//材质颜色
transparent: false, //是否开启透明度
// opacity: 0.5, //材质透明度
shininess: 20, //高光部分的亮度,默认30
specular: 0x000000, //高光部分的颜色
})
// 4.创建网格模型
const mesh = new THREE.Mesh(geometry, material)
// 5.将模型添加到场景
scene.add(mesh)
/** ———————————————————创建辅助坐标系———————————————————*/
const axeHeleper = new THREE.AxesHelper();
scene.add(axeHeleper)
// mesh.add(axeHeleper)
/** —————————————————创建相机————————————————*/
const camera = new THREE.PerspectiveCamera(
45,//视觉角度
1,
1,
3000 // 1:近裁截面, 3000:远裁截面
)
camera.position.set(300, 300, 300)
camera.lookAt(0, 0, 0)
/**————————————————————渲染器———————————————————————*/
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.setClearColor(0x444444, 0.2)
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
/** —————————引入轨道控制器扩展库OrbitControls.js—————————*/
const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 0, 0)
controls.update() //更新控制器状态
// 监听控制器变化,触发重新渲染
controls.addEventListener('change', () => {
renderer.render(scene, camera)
})
const pointLight = new THREE.DirectionalLight(0xffffff, 1.0);
// 平行光辅助观察
const pointLightHelper = new THREE.DirectionalLightHelper(pointLight, 10);
// 光源衰减度
pointLight.decay = 0;
// 设置光源点位置
pointLight.position.set(100, 100, 100); ///点光源放在x轴上
scene.add(pointLight)
scene.add(pointLightHelper);
// 根据容器尺寸更新画布
const resize = () => {
const { clientWidth, clientHeight } = container
if (!clientWidth || !clientHeight)
return
camera.aspect = clientWidth / clientHeight
camera.updateProjectionMatrix()
renderer.setSize(clientWidth, clientHeight)
container.appendChild(renderer.domElement)
renderer.render(scene, camera)
}
// 渲染函数,给 GUI 复用
const render = () => {
renderer.render(scene, camera)
}
window.addEventListener('resize', resize)
resize()
return {
scene,
mesh,
material,
camera,
renderer,
render,
destroy: () => {
window.removeEventListener('resize', resize)
controls.dispose()
geometry.dispose()
material.dispose()
renderer.dispose()
if (renderer.domElement.parentNode === container)
container.removeChild(renderer.domElement)
},
}
}
文件3. 该文件引入了gui.js 方便更改 材质,颜色,以及xyz轴上的位置 ,方便学习和使用
/*—————————定义方法————————— */
import type { TypePositionList } from '@/store/three/type'
import * as THREE from 'three'
type GuiOptions = {
dat: typeof window.dat
mesh: THREE.Mesh
material: THREE.material
scene: THREE.Scene
camera: THREE.Camera
renderer: THREE.WebGLRenderer
render: () => void
}
export function createGui(options: GuiOptions) {
const { dat, mesh, scene, camera, renderer, render, material } = options
// 创建 GUI
const gui = new dat.GUI()
// 材质分组
const matFolder = gui.addFolder('材质')
matFolder.close()
// 位置分组
const positionFolder = gui.addFolder('位置X,Y,Z')
// 颜色分组
const ColirFolder = gui.addFolder('物体颜色值')
// 更改位置信息
const positionList = (list: TypePositionList[]) => {
for (const item of list)
positionFolder.add(item.position, item.axle, item.start, item.over).onChange(render)
}
// 更改几何体形状
const geometryType = () => {
const obj = {
scale: 'SphereGeometry',
}
matFolder
.add(obj, 'scale', {
圆形: 'SphereGeometry',
长方体: 'BoxGeometry',
圆柱体: 'CylinderGeometry',
圆锥: 'ConeGeometry',
矩形平面: 'PlaneGeometry',
圆平面: 'CircleGeometry',
})
.name('几何体形状')
.onChange((value) => {
const geometryMap: Record<string, () => THREE.BufferGeometry> = {
SphereGeometry: () => new THREE.SphereGeometry(100, 100, 100),
BoxGeometry: () => new THREE.BoxGeometry(100, 100, 100),
CylinderGeometry: () => new THREE.CylinderGeometry(100, 100, 100),
ConeGeometry: () => new THREE.ConeGeometry(100, 100, 100),
PlaneGeometry: () => new THREE.PlaneGeometry(100, 100, 100),
CircleGeometry: () => new THREE.CircleGeometry(100, 100, 100),
}
const geometry = geometryMap[value]?.()
if (geometry) {
mesh.geometry.dispose()
mesh.geometry = geometry
renderer.render(scene, camera)
}
})
}
// 更改材质
const textureType = () => {
const obj = {
scale: 'MeshBasicMaterial',
}
const materialParams = {
color: 0x00ff00,
transparent: false,
shininess: 20,
specular: 0x000000,
}
matFolder
.add(obj, 'scale', {
网格基础: 'MeshBasicMaterial',
网格漫反射: 'MeshLambertMaterial',
网格高光: 'MeshPhongMaterial',
物理1: 'MeshStandardMaterial',
物理2: 'MeshPhysicalMaterial',
点材质: 'PointsMaterial',
线基础: 'LineBasicMaterial',
精灵: 'SpriteMaterial',
})
.name('材质Material')
.onChange((value) => {
const materialMap: Record<string, () => THREE.Material> = {
MeshBasicMaterial: () => new THREE.MeshBasicMaterial(materialParams),
MeshLambertMaterial: () => new THREE.MeshLambertMaterial(materialParams),
MeshPhongMaterial: () => new THREE.MeshPhongMaterial(materialParams),
MeshStandardMaterial: () => new THREE.MeshStandardMaterial(materialParams),
MeshPhysicalMaterial: () => new THREE.MeshPhysicalMaterial(materialParams),
PointsMaterial: () => new THREE.PointsMaterial(materialParams),
LineBasicMaterial: () => new THREE.LineBasicMaterial(materialParams),
SpriteMaterial: () => new THREE.SpriteMaterial(materialParams),
}
const materials = materialMap[value]?.()
if (materials) {
mesh.material.dispose()
mesh.material = materials
renderer.render(scene, camera)
}
})
}
// 更改几何体颜色
const colorType = () => {
ColirFolder.addColor({ color: 0x00ffff }, 'color').onChange(function (value) {
material.color.set(value);
renderer.render(scene, camera);
});
}
// 销毁 GUI
const destroy = () => {
gui.destroy()
}
return {
colorType,
positionList,
geometryType,
textureType,
destroy,
}
}
四、Three.js 的基础组成
我们将逐步拆解核心对象,每个部分都有代码示例帮助你理解。
1. 场景 Scene
场景是 3D 世界的“舞台”,所有模型、灯光都要加入其中。没有场景,其他对象无处安放。
const scene = new THREE.Scene()
2. 几何体 Geometry
几何体决定“物体长什么样”。常见类型包括:
BoxGeometry:立方体SphereGeometry:球体CylinderGeometry:圆柱体ConeGeometry:圆锥体PlaneGeometry:平面CircleGeometry:圆形
在封装代码中,默认使用盒子几何体(支持 GUI 切换):
const geometry = new THREE.BoxGeometry()
几何体是 Three.js 的起点,所有复杂模型都基于顶点数据构建。
3. 材质 Material
材质决定物体“怎么看起来”,影响颜色、光照、透明度等效果。常用材质包括:
MeshBasicMaterial:基础材质,无视光的影响MeshLambertMaterial:基于漫反射,模拟非金属MeshPhongMaterial:高光材质,增加光泽感MeshStandardMaterial:物理渲染材质,推荐使用
示例中使用 MeshPhongMaterial:
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
transparent: false,
shininess: 20,
specular: 0x000000,
})
参数解释:
color:物体基础颜色(十六进制值,如0x00ff00代表绿色)transparent:控制透明度(true/false)shininess:高光强度specular:高光颜色
初学者建议从 MeshBasicMaterial 和 MeshPhongMaterial 入手。
4. 网格 Mesh
网格是几何体和材质的组合,代表实际显示在场景中的物体:
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
至此,物体正式进入场景。
5. 相机 Camera
相机决定观看角度,常用 PerspectiveCamera(透视相机):
const camera = new THREE.PerspectiveCamera(45, 1, 1, 3000)
camera.position.set(300, 300, 300)
camera.lookAt(0, 0, 0)
参数含义:
45:视角大小(度)1:宽高比(初始值,后随容器更新)1:近裁剪面(小于此距离的对象不渲染)3000:远裁剪面(大于此距离的对象不渲染)lookAt(0, 0, 0):相机朝向原点,方便观察中心物体
6. 渲染器 Renderer
渲染器负责将 3D 场景画到页面中:
const renderer = new THREE.WebGLRenderer({
antialias: true,
})
renderer.setClearColor(0x444444, 0.2)
antialias: true:开启抗锯齿,边缘更平滑setClearColor:设置背景色(如0x444444表示深灰色)
最后必须执行渲染:
renderer.render(scene, camera)
这是“画画”的关键步骤。
五、为什么要加灯光
新手常见问题:模型创建后页面黑乎乎的?原因:许多材质依赖光照显示效果。示例中添加环境光和方向光:
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
const pointLight = new THREE.DirectionalLight(0xffffff, 1.0)
pointLight.position.set(100, 100, 100)
scene.add(pointLight)
环境光 AmbientLight
提供整体亮度,均匀覆盖场景,不产生明显阴影:
new THREE.AmbientLight(0xffffff, 1) // 颜色 + 强度
方向光 DirectionalLight
类似太阳光,有明确方向和阴影效果:
new THREE.DirectionalLight(0xffffff, 1)
pointLight.position.set(100, 100, 100) // 位置设置
调试时可加入辅助器:
const pointLightHelper = new THREE.DirectionalLightHelper(pointLight, 10)
scene.add(pointLightHelper)
六、OrbitControls 让页面“能转起来”
OrbitControls 是教学必备,它支持用户通过鼠标交互查看场景:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 0, 0)
controls.update()
功能:
- 鼠标拖拽:旋转视角
- 滚轮:缩放
- 右键:平移
教学场景中,这有助于直观观察模型。添加事件监听以动态渲染:
controls.addEventListener('change', () => {
renderer.render(scene, camera)
})
每次用户交互后,重新渲染更新画面。这样,你的基础 3D 场景就完整了!通过封装在 Vue 组件中,应用更易维护。
结语
通过这教程,你学会了搭建 Three.js 核心流程、集成到 Vue 项目,并处理挂载/销毁逻辑。后续可扩展:添加自定义几何体、实现动画或优化性能。实践建议:在小项目中应用这些概念,逐步学习 GUI 和高级材质的使用。有问题随时参考 Three.js 官方文档或在社区提问!
更多推荐

所有评论(0)