Skip to content

three.js 搭建+vue3

1、简介

地址:

threejs官网

JS
https://threejs.org/

three.js中文官网

JS
https://www.webgl3d.cn/

github各个版本:

JS
// 官方英文网地址
https://github.com/mrdoob/three.js/tags


//中文网地址
http://www.webgl3d.cn/pages/aac9ab/

github地址:

threejs官方文件包

JS
// threejs官方文件包地址
https://github.com/mrdoob/three.js/releases


//threejs官方文件包所有版本
https://github.com/mrdoob/three.js/releases


// Threejs中文网(电子书课件)
http://www.webgl3d.cn

// Threejs官网中文文档链接
https://threejs.org/docs/index.html#manual/zh/

概念

three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能

什么是threejs,很简单,你将它理解成three + js就可以了。three表示3D的意思,js表示javascript的意思。那么合起来,three.js就是使用javascript来写3D程序的意思。Javascript是运行在网页端的脚本语言,那么毫无疑问Three.js也是运行在浏览器上的

three.js其实是依靠与Canvas画布的3D绘图功能,threejs渲染的三维效果图就是呈现在Canvas画布上面。

功能

Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。你可以在它的主页上看到许多精彩的演示。

three.js所包含的知识和技巧

image.png

目录简介

image.png

Build目录:主要的两个文件three.min.js,three.js。

Docs目录:这里是three.js的帮助文档,里面是各个函数的api,可惜并没有详细的解释。试图用这些文档来学会three.js是不可能的。

Editor目录:一个类似3D-max的简单编辑程序,它能创建一些三维物体。

Examples目录:一些例子demo,可惜没有文档介绍。对图像学理解不深入的同学,学习成本非常高。

Src目录:源代码目录,里面是所有源代码。

Test目录:一些测试代码,基本没用

2、安装使用

three.js 安装

js
在vue3之中安装使用 
yarn add three --save

npm的方式
npm install --save three 

vite npm install --save-dev vite

(这里我采用的yarn安装,全面拥抱yarn,确实比那个npm好用,哈哈 ^_^)

CDN

js
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
<script type="importmap">{ "imports": { "three": "https://unpkg.com/three@<version>/build/three.module.js", "three/addons/": "https://unpkg.com/three@<version>/examples/jsm/" } } 
</script>

3、实现一个简单的3D球体

接下来我们创建一个简单的3D球体,并添加到场景中。

先看看我们最后实现的效果

image.png

代码如下:

构建我们的结构

js
<template>
  <div ref="container"></div>
</template>

(1)引入我们需要的元素并且创建变量来进行存储three之中的场景,相机,渲染器以及球体这个对象

  • 使用 script setup 语法进行组件的设置。
  • 引入了 Vue 3 的 onMountedref 方法,以及 Three.js 库中的 THREE 对象。
  • 创建了一些变量来存储 Three.js 中的场景、相机、渲染器和球体对象。

(2)引入我们生命周期钩子函数并且写一个事件监听

  • onMounted 钩子:组件挂载到 DOM 后立即执行用于设置 Three.js 场景、相机和渲染器,这个时候同时我们开始动画循环。
  • animate 函数:球体转动的每一帧过程之中更新球体的旋转角度,使用渲染器将场景渲染到页面上。
  • onWindowResize 函数:窗口大小调整时更新相机的长宽比例和渲染器的大小,让内容适应新的窗口大小。

(3)总结

上面整个过程就是我们创建一个 Vue 3 组件,渲染一个带有旋转效果的 3D 球体,实现了基本的 3D 绘制和动画。

接下来看一下我们的代码然后不断理解其中带给我们的概念。

js
<script setup>
import { onMounted, ref } from 'vue';
import * as THREE from 'three';

// 创建对 DOM 元素的引用
const container = ref(null);
let scene, camera, renderer, sphere;

onMounted(() => {
  // 创建场景
  scene = new THREE.Scene();

  // 创建相机
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 5;

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.value.appendChild(renderer.domElement);

  // 创建一个球体
  const geometry = new THREE.SphereGeometry(1, 32, 32);
  const material = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
  sphere = new THREE.Mesh(geometry, material);
  scene.add(sphere);

  // 动画循环
  function animate() {
    requestAnimationFrame(animate);
    
    // 旋转球体
    sphere.rotation.x += 0.01;
    sphere.rotation.y += 0.01;

    renderer.render(scene, camera);
  }
  animate();
  // 处理窗口大小调整
  window.addEventListener('resize', onWindowResize);

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }
});
</script>

4、具体概念和参数的理解

先来看看我们通过参数调整的图形效果

image.png

4-0、canvas画布

这里我们直接跳过这个画布概念,做前端的应该都知道

接下来我们学习一些three之中的概念,这些概念能够帮助我们更好的理解three

4-1、三维场景Scene

理解three的理念。首先要理解我们的第一个概念--场景

在three之中,场景可以说是所有部分的起始,就类似我们构建一个虚拟世界,首先得有一个场景,故事才开始娓娓道来一样

three之中场景好比容器,将对象、模型、粒子、灯光等放入指定场景之中

js
const scene = new THREE.Scene()

完整代码我们打印输出看看这个场景究竟是什么

js
<script src="./three.js"></script>
<script>
        //随便输入一个API,测试下是否已经正常引入three.js
        console.log(THREE.Scene); 
</script>
JS
class Scene extends Object3D {
  constructor() {
    super(); // 调用父类的构造函数
    this.isScene = true; // 标识是否为场景对象
    this.type = 'Scene'; // 类型标识
    this.background = null; // 背景(默认为 null)
    this.environment = null; // 环境(默认为 null)
    this.fog = null; // 雾效(默认为 null)
    this.overrideMaterial = null; // 覆盖材质(默认为 null)
    this.autoUpdate = true; // 是否自动更新(默认为 true)
  }

  // 设置背景方法
  setBackground(background) {
    this.background = background;
  }

  // 设置环境方法
  setEnvironment(environment) {
    this.environment = environment;
  }

  // 设置雾效方法
  setFog(fog) {
    this.fog = fog;
  }

  // 设置覆盖材质方法
  setOverrideMaterial(material) {
    this.overrideMaterial = material;
  }

  // 设置自动更新
  setAutoUpdate(autoUpdate) {
    this.autoUpdate = autoUpdate;
  }

  // 更新方法,通常在每帧渲染时调用
  update() {
    if (this.autoUpdate) {
      // 在此处加入你需要每帧更新的内容
      console.log("Scene is updating...");
    }
  }
}

这里我们可以看出,其实就是简单得一个类得函数,其中包含了我们需要的各种东西

那么场景如何确定你在页面上得位置呢,这个时候就需要用到threejs得三维坐标系

threejs三维坐标系

哲理threejs里面定位使用的是三维直角坐标系(其实就是我们初高中三位坐标里面得---三维笛卡尔坐标系)去定位。

还记得初高中老师教的我们拿手指头去衡量吗?就跟下图之中所示的差不多

image.png

对象

创造了场景,这个时候我们就可以想着往里面加点什么了,加的这个对象就是threejs之中的对象的概念,可以是自己创造的几何体,也可以是我们的网络模型

几何体

这里我们虽然创建了几何体,但是就跟画画一样,你画的东西,现在是无色的啊

image.png

所以其实我们整个过程其实是:

创建一个几何体(Geometry)和材质(Material),并将它们组合成一个网格对象(Mesh)

也就是类似下面这段代码

js
//1. 长方体形状
const geometry = new THREE.BoxGeometry(100, 100, 100); 
//2. 长方体材质
const material = new THREE.MeshBasicMaterial({
    color: 0x0000ff,
}); 
//3. 长方体网格模型Mesh
const mesh = new THREE.Mesh(geometry, material);
//4. 长方体添加到虚拟场景中
scene.add(mesh);

这里我们需要注意的就是这个颜色值应该是一个十六进制格式的

js
十六进制颜色代码中,金色的标准表示是 `#FFD700`,所以我们这里使用的就是
const material = new THREE.MeshBasicMaterial({ color: 0xFFD700 });

4-2 相机Camera

想把我们之前创建的三维场景Scene渲染到web网页上,还需要定义一个虚拟相机Camera

怎么理解这个相机呢,你可以想象成你需要对上面的那个几何体拍个照片给别人,否则别人也看不到啊。

透视投影相机PerspectiveCamera

如何实例化一个透视投影相机呢

js
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();

//这里我们使用一下

// 创建相机
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

4-3 WebGL渲染器WebGLRenderer

有了场景,也有了几何体,手上也有了相机,那这个时候,总要有个人用相机吧,渲染器WebGLRenderer就是做这个用处的

创建

js
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer({
    canvas:canvas//渲染结果输出画布:canvas
});

渲染

js
renderer.render(scene, camera); //执行渲染操作

渲染器Canvas画布属性.domElement

这里我们是进行了渲染,那如何进行相关的canvas样式的更改呢?

.domElement本质上就是一个HTML元素:Canvas画布

那这里我们如何插入呢,这里我们插入页面之中试试(其实就是插入你之前拍下的照片)

js
document.body.appendChild(renderer.domElement);

4-4 动画渲染循环

threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染

看看官方给我们的介绍,这一看,可不就是直接调用的请求动画帧被

js
// requestAnimationFrame实现周期性循环执行
function render() {
    requestAnimationFrame(render);//请求再次执行函数render
}
render();

概念我们都学完了,那接下来就拿个html写个旋转的不管啥先瞅瞅呗

看,是不是跟我们开头的几乎一模一样,所以知道了基础的一些概念,自己写一个球体已经很简单了。

js
.<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	<!-- <canvas style="background: black;" width="800" height="800"></canvas> -->
	<script src="./three.js"></script>
	<script>
 

	// 创建场景 Scene
        const scene = new THREE.Scene();
        console.log(THREE.Scene); //随便输入一个API,测试下是否已经正常引入three.js


        // 创建透视相机
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.z = 5;

        // 创建 WebGL 渲染器
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        // canvas.appendChild(renderer.domElement);

     
        const geometry = new THREE.SphereGeometry(1, 32, 32); // 创建一个几何体
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 , wireframe: true}); // 创建一个基本材质并设置颜色
        const cube = new THREE.Mesh(geometry, material);// 将几何体和材质组合成一个网格对象
        scene.add(cube);// 将立方体添加到场景中

        // 创建动画循环
        function animate() {
            requestAnimationFrame(animate);

            // 使立方体旋转
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;

            // 渲染场景和相机
            renderer.render(scene, camera);
        }

        // 开始动画循环
        animate();
	</script>
</body>
</html>

image.png

4-5 vue3之中使用(ref方式)

在普通的html页面之中我们使用没问题,那么接下来我们就该挪进去vue3组件之中了,那这个时候我们又改如何使用呢?

vue3组件之中还是一样,只不过vue3需要通过ref标记获取HTML元素方式

下面就是我们的元素

js
<script setup>
import { ref } from 'vue'
const webgl = ref(null);
</script>
<template>
  <div ref="webgl">
  </div>
</template>


<script setup>
import { ref } from 'vue'
import { onMounted } from 'vue'

import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30,width/height,1,3000);
// WebGL渲染器
const renderer = new THREE.WebGLRenderer();
...
// 原来canvas直接插入body代码删除
// document.body.appendChild(renderer.domElement);
export default renderer;

const webgl = ref(null);
onMounted(() => {
  //webgl.value:表示ref值为webgl的div元素
  webgl.value.appendChild(renderer.domElement);
});
</script>




后面可以抽离出我们经常用多的部分
import renderer from './twin.js';//获取到threejswebgl的渲染器对象

看看我们的效果,调整一下颜色

js
renderer.setClearColor(0xFFFFFF);  // 设置背景颜色为金色

直接使用我们的渲染器给渲染个呗,ok

image.png

5、进阶

普通的一个我们实现了,那么接下来我们给他加一些花里胡哨的操作

从右侧加个光模拟一下大爆炸的感觉

image.png

js
 // 设置场景、相机和渲染器
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 创建一个简单的立方体以观察光照效果
        const geometry = new THREE.BoxGeometry();
        const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 ,antialias: true});
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

        // 添加地面
        const planeGeometry = new THREE.PlaneGeometry(200, 200);
        const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x808080, side: THREE.DoubleSide });
        const plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.rotation.x = Math.PI / 2;  // 使平面水平放置
        plane.position.y = -1;  // 调整平面位置
        scene.add(plane);



        // 创建一个球体作为光源的表示
        const sphereGeometry = new THREE.SphereGeometry(1, 10, 10);// 创建一个几何体
        const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xFFD700 , wireframe: true}); // 创建一个基本材质并设置颜色
        const lightSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);  // 将几何体和材质组合成一个网格对象
        scene.add(lightSphere);

        // 添加点光源
        const pointLight = new THREE.PointLight(0xffffff, 1, 100);
        scene.add(pointLight);

        // 将光源和球体移动到初始位置(右上角)
        lightSphere.position.set(2, 2, 2);
        pointLight.position.copy(lightSphere.position);

        camera.position.z = 5;

        // 动画函数
        function animate() {
            requestAnimationFrame(animate);

            // 光源向下移动
            lightSphere.position.y -= 0.01;
            pointLight.position.copy(lightSphere.position);

            renderer.render(scene, camera);
        }

        animate();

只有细细体会,自己才能感觉到,3D的世界!

Released under the MIT License.