news 2026/7/12 12:06:58

THREE+VUE3+VITE THREE.JS基础教学

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 步:

  1. 创建场景Scene:3D 世界的“舞台”。
  2. 创建几何体Geometry:决定物体的形状。
  3. 创建材质Material:定义物体的外观。
  4. 创建网格模型Mesh:几何体和材质的组合,代表真实的物体。
  5. 创建相机和渲染器:设置观看角度并渲染到 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:高光颜色

初学者建议从MeshBasicMaterialMeshPhongMaterial入手。

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 官方文档或在社区提问!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 8:28:05

计算机毕业设计之基于深度学习的投诉文本分类系统

基于深度学习的投诉文本分类系统利用先进的自然语言处理技术&#xff0c;能够高效地对海量投诉文本进行自动分类。该系统首先采用预训练的语言模型对文本进行嵌入表示&#xff0c;然后将这些嵌入输入到神经网络中进行分类。实验结果表明&#xff0c;该方法在多个公开数据集上的…

作者头像 李华
网站建设 2026/7/6 8:28:02

Python自动化脚本部署指南:从环境配置到实战排错

1. 先搞清楚 biliTickerBuy 到底解决什么问题如果你在找能自动处理B站相关票务的工具&#xff0c;那mikumifa / biliTickerBuy这个项目值得先看一眼。它不是一个通用的抢票软件&#xff0c;核心目标非常明确&#xff1a;针对B站特定活动&#xff08;如会员购、线下演出、展览等…

作者头像 李华
网站建设 2026/7/11 8:01:34

阿里云RDS大规模降本实践_预留实例读写分离存储压缩

一句话答案&#xff1a;大规模使用云数据库降本的核心方法是"预留实例券&#xff08;RI&#xff09; 读写分离 存储压缩 备份归档 Serverless 资源池化"六大杠杆组合。阿里云 RDS 作为国内市场份额第一的云关系型数据库&#xff0c;通过包年包月RI 组合&#xff…

作者头像 李华
网站建设 2026/7/6 1:30:00

G-Helper:重新定义华硕笔记本性能控制的轻量级神器

G-Helper&#xff1a;重新定义华硕笔记本性能控制的轻量级神器 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Exper…

作者头像 李华
网站建设 2026/7/8 18:31:17

Appium自动化测试中pytest-repeat插件的集成与应用实践

1. 项目概述&#xff1a;为什么需要重复执行测试&#xff1f;在移动端自动化测试的日常工作中&#xff0c;我们经常会遇到一些“玄学”问题&#xff1a;某个测试用例在本地跑十次都成功&#xff0c;一到线上集成环境就偶发性失败&#xff1b;或者某个涉及复杂网络交互、内存占用…

作者头像 李华
网站建设 2026/7/8 17:50:31

CasaOS深度体验:个人云服务器从零搭建到稳定运维全指南

上周帮朋友折腾一台闲置的旧笔记本&#xff0c;想把它变成一个家庭影音和文件共享中心。他提了几个很具体的要求&#xff1a;界面要简单&#xff0c;最好像手机App一样点一下就能装软件&#xff1b;能方便地管理硬盘&#xff0c;把电影、照片自动分类&#xff1b;最好还能跑点D…

作者头像 李华