一、Threejs简介
Three.js是一款运行在浏览器中的3D引擎,你可以在网页中创建和显示动画的3D计算机图形。它是一个开源项目,其目标是创建一个易于使用,轻量级,可移植的3D库。Three.js以WebGL为基础,封装了底层的WebGL API,提供了更简洁易用的3D API接口,让开发者能够更方便地创建和显示3D图形。
1.1 Threejs核心概念
-
场景(scene):
场景是Three.js中所有对象的容器,可以容纳图形元素,如地面、天空、光照、人、动物等。场景相当于一个3D世界的宇宙,图形元素则是其中的星星。只有添加到场景中的图形元素,其坐标、大小等才有意义。
-
相机(camera):
相机用于拍摄场景中的画面,决定了观察者在3D世界中的视角。相机的位置和角度不同,拍摄出来的画面就不一样。Three.js提供了多种类型的相机,如透视相机(PerspectiveCamera)和正交相机(OrthographicCamera),以满足不同的视觉需求。
-
渲染器(renderer):
渲染器的作用是将相机拍摄出来的画面绘制到HTML5画布上。Three.js提供了几种不同的渲染器,包括WebGLRenderer、CanvasRenderer和SVGRenderer等。
-
物体(Objects):
3D世界是由一个个物体组合而成的,这些物体可以是人、车、楼等。在Three.js中,物体是通过几何形状、材料和属性等组合而成的。
-
光源(Light):
光源用于模拟真实世界中的光照效果。通过设置光源的位置和颜色,可以改变场景中物体的亮度和阴影效果,使得物体看起来更加真实。
二、第一个Threejs应用
2.1 代码
<template>
<div ref="canvasBox"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import * as THREE from 'three'
// 创建场景
const scene = new THREE.Scene()
// 创建相机
const camera = new THREE.PerspectiveCamera(
75, // 摄像机视锥体垂直视野角度
window.innerWidth / window.innerHeight, // 摄像机视锥体长宽比
0.1, // 摄像机视锥体近端面
100 // 摄像机视锥体远端面
)
// 创建渲染器
const renderer = new THREE.WebGL1Renderer()
render.setSize(window.innerWidth, window.innerHeight)
// 将渲染器的canvas画布,添加到指定容器中
const canvasBox = ref(null)
onMounted(() => {
canvasBox.value.appendChild(render.domElement)
})
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1)
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: '#ff0000' })
// 创建物体
const cube = new THREE.Mesh(geometry, material)
// 将物体添加到场景中
scene.add(cube)
// 设置相机位置
camera.position.z = 5
// 渲染函数
const animate = () => {
cube.rotation.x += 0.01
cube.rotation.y += 0.01
// 渲染器渲染
renderer.render(scene, camera)
requestAnimationFrame(animate)
}
animate()
</script>
<style scoped lang="less"></style>
2.2 重点知识
- 创建一个物体需要先创建几何体和材质
- 物体需要添加到场景中才能显示
- 相机初始位置在(0, 0,0 ),需要调整位置才能看到物体
- 渲染器需要接收场景和相机
- 调用一次渲染器的render方法,会绘制一帧,要想看到动画,需要不断调用render方法
三、场景小工具
3.1 AxesHelper(坐标系)
AxesHelper用于简单模拟3个坐标轴
接收一个参数,表示坐标系长度
// 创建坐标轴
const axesHelper = new THREE.AxesHelper(20)
// 将坐标轴添加到场景
scene.add(axesHelper)
3.2 OrbitControls(轨道控制器)
Orbit controls(轨道控制器)可以使得相机围绕目标进行轨道运动。
-
导入并创建轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 创建轨道控制器 const orbitControls = new OrbitControls(camera, render.domElement)
-
更新轨道控制器(不更新好像也可以拖动)
// 渲染函数 const animate = () => { // 更新物体位置 cube.rotation.x += 0.01 cube.rotation.y += 0.01 // 更新轨道控制器位置 orbitControls.update() // 渲染器渲染 renderer.render(scene, camera) requestAnimationFrame(animate) } animate()
-
设置阻尼效果
// 设置阻尼效果 orbitControls.enableDamping = true // 设置阻尼系数 orbitControls.dampingFactor = 0.05
-
设置轨道控制器自动旋转
// 设置自动旋转 orbitControls.autoRotate = true
四、三维向量(Vector3)
该类表示的是一个三维向量(3D vector)。 一个三维向量表示的是一个有顺序的、三个为一组的数字组合(标记为x、y和z), 可被用来表示很多事物,例如:
- 一个位于三维空间中的点。
- 一个在三维空间中的方向与长度的定义。
- 任意的、有顺序的、三个为一组的数字组合。
4.1 修改物体位置
坐标位置是局部坐标,以父元素为参照
-
使用set方法修改
// 通过set方法修改位置 cube.position.set(2, 0, 0)
-
直接修改
// 直接修改 cube.position.x = 3
4.2 缩放
缩放也是以父元素为参照,父元素放大,子元素也会放大
-
通过set方法缩放
// 縮放 cube.scale.set(2, 2, 1)
五、欧拉角(Euler)
欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体。
对 Euler 实例进行遍历将按相应的顺序生成它的分量 (x, y, z, order)。
5.1 旋转
旋转也是相对父元素
-
使用方法设置
// 旋转 cube.rotateX(Math.PI / 4)
-
直接设置
cube.rotation.y = Math.PI / 4
六、画布自适应和全屏
-
监听窗口变化,来更新画布和相机参数
// 监听窗口变化 window.addEventListener("resize", () => { // 更新画布大小 renderer.setSize(window.innerWidth, window.innerHeight) // 更新相机宽高比 camera.aspect = window.innerWidth / window.innerHeight // 更新相机投影矩阵 camera.updateProjectionMatrix() })
-
全屏
// 全屏 const fullScreen = () => { renderer.domElement.requestFullscreen() }
七、GUI
7.1 基本使用
-
导入gui
import {GUI} from 'three/examples/jsm/libs/lil-gui.module.min'
-
使用GUI调用方法
// 退出全屏 const exitFullscreen = () => { renderer.domElement.exitFullscreen() } // 配置GUI let guiOption = { fullScreen, exitFullscreen } // 创建GUI const gui = new GUI() // add方法第一个参数是一个对象,第二个参数是对象的某个属性 // name方法设置名称 gui.add(guiOption, "fullScreen").name("全屏") gui.add(guiOption, "exitFullscreen").name("退出全屏")
-
使用GUI修改物体属性
// 第3和4个参数控制范围 gui.add(cube.position, "x", -5, 5).name("x位置") gui.add(cube.position, "y").min(-10).max(10).step(1).name("y位置")
7.2 创建组
-
使用addFolder方法创建一个组
const folder = gui.addFolder("位置") folder.add(cube.position, "x", -5, 5).name("x位置") folder.add(cube.position, "y").min(-10).max(10).step(1).name("y位置")
7.3 获取修改值
-
使用onChange获取改变后的值,一修改就触发
folder.add(cube.position, "x", -5, 5).name("x位置").onChange(x => { console.log(x); })
-
使用onFinishChange获取改变后的值,停止修改后才触发
folder.add(cube.position, "x", -5, 5).name("x位置").onFinishChange(x => { console.log(x); })
7.4 改变颜色
-
使用addColor添加
// 定义对象 let color = { cubeColor: '#ff0000' } // 修改对象的属性,在change事件中,修改材质的颜色 gui.addColor(color, 'cubeColor').onChange(color => { cube.material.color.set(color) })