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 步创建场景Scene3D 世界的“舞台”。创建几何体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 classneirong div classthree-box refthreeJSDom stylewidth: 100%; height: 100%;/div /div /template script setup langts import { onMounted, onUnmounted, ref } from vue import { initThree } from /unit/three/thress import { createGui } from /unit/three/guiJs const threeJSDom refHTMLDivElement | null(null) let threeApi: ReturnTypetypeof initThree | null null let guiApi: ReturnTypetypeof 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: Recordstring, () 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: Recordstring, () 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物理渲染材质推荐使用示例中使用MeshPhongMaterialconst material new THREE.MeshPhongMaterial({ color: 0x00ff00, transparent: false, shininess: 20, specular: 0x000000, })参数解释color物体基础颜色十六进制值如0x00ff00代表绿色transparent控制透明度true/falseshininess高光强度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 官方文档或在社区提问