官网:Three.js – JavaScript 3D Library
1.threeJs三要素
1.场 景:放置物体的容器
import * as THREE from 'three'
let scene = new THREE.Scene()
2.摄像机:类似人眼,可调位置,角度等信息,展示不同画面
// 参数1:垂直角度(建议 75),视野范围
// 参数2:宽高比(建议与画布相同宽高),物体绘制比例
// 参数3:近截面距离摄像机距离
// 参数4:远截面距离摄像机距离
let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
摄像机 4 个参数讲解:
-
角度:视野垂直范围,越大看到越多,物体越小
-
宽高比:影响图形拉伸/压缩,摄像机宽高比 = 画布宽高比(标准图形)
-
近截面:近边界
-
远截面:远边界
3.渲染器:接收场景和摄像机,计算在浏览器上渲染的最终 2D 画面
//创建渲染器
let renderer = new THREE.WebGLRenderer()
例如(以下代码俊以vue3为例):
下包:npm i three
<template>
<div id="threeID">
</div>
</template>
<script setup>
// 1. 引入 three 库
import * as THREE from 'three'
// 创建场景,摄像机,渲染器
let scene, camera, renderer
function init() {
// 2. 创建场景对象
scene = new THREE.Scene()
// 3. 创建摄像机对象
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
// 4. 创建渲染器,并设置画布大小,添加到 DOM 显示
renderer = new THREE.WebGLRenderer()
// 设置画布大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 把画布 canvas 标签添加到id为threeID上
document.querySelector('#threeID').append(renderer.domElement)
// 传入场景和摄像机,渲染画面
renderer.render(scene, camera)
}
onMounted(() => {
init()
})
</script>
<style scoped></style>
2.three.js立方体
1. 创建图形,宽高深为 1 单位(立方缓冲几何体)
const geometry = new THREE.BoxGeometry(1, 1, 1);
2. 创建材质,颜色为绿色 0x00ff00 (网格基础材质-线面纯颜色描绘表面)
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
3. 创建网格物体对象,传入图形和材质(网格物体对象)
const cube = new THREE.Mesh(geometry, material);
4. 把创建的物体加入到场景中
scene.add(cube);
整个代码:
function createCube() {
// 1. 创建图形,宽高深为 1 单位(立方缓冲几何体)
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 2. 创建材质,颜色为绿色 0x00ff00 (网格基础材质-线面纯颜色描绘表面)
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 3. 创建网格物体对象,传入图形和材质(网格物体对象)
const cube = new THREE.Mesh(geometry, material);
// 4. 把物体加入到场景中
scene.add(cube);
}
onMounted(() => {
init()
createCube()
renderer.render(scene, camera)
})
移动摄像机向z轴5个单位(默认摄像机和物体在原点)
camera.position.z = 5
更多材质和几何体需要查询官方文档
3.three.js轨道控制器
目的:使摄像机围绕目标进行轨道运动,可以右键拖动,左键旋转,滚轮拉进,拉远摄像机。
1.单独引入 OrbitControls 轨道控制器构造函数
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
2.创建轨道控制器
let controls
controls = new OrbitControls( camera, renderer.domElement )
3.在渲染循环中更新场景渲染
function renderLoop() {
//在渲染循环中更新场景渲染
renderer.render(scene, camera)
// 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
controls.update()
// 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
// 好处:当前页面切换到后台,暂停递归
requestAnimationFrame(renderLoop)
}
renderLoop()
4.three.js坐标轴
目的:一个 x,y,z 方向的坐标轴辅助对象 ,帮我们理解物体在世界坐标系中位移,缩放,渲染等效果,可以辅助我们开发和调试。
function createHelper() {
// 1. 创建坐标轴对象,设置长度
const axesHelper = new THREE.AxesHelper(5)
// 2. 添加到场景中
scene.add(axesHelper)
}
createHelper()
5.three.js轨道控制器-控制效果
目的:调整轨道控制器属性,影响摄像机细节
阻尼效果(带有缓冲效果):new OrbitControls(camera, renderer.domElement).enableDamping = true
开启自动旋转轨道控制器效果:new OrbitControls(camera, renderer.domElement).autoRotate = true
function controlsCreate() {
// 创建轨道控制器
controls = new OrbitControls(camera, renderer.domElement)
// 1. 阻尼效果
controls.enableDamping = true
// 2. 开启自动旋转轨道控制器效果->带动摄像机一起旋转(摄像机顺时针水平旋转)
// controls.autoRotate = true
// 3. 垂直角度范围控制(0 上面,Math.PI 下面)
controls.maxPolarAngle = Math.PI
controls.minPolarAngle = 0
// 水平角度范围控制
controls.maxAzimuthAngle = 1.5 * Math.PI
controls.minAzimuthAngle = 0.5 * Math.PI
// 4. 摄像机移动范围控制
controls.minDistance = 2
controls.maxDistance = 10
}
更多属性查看官方文档:
6. 优化-适配场景大小
目的:浏览器窗口,尺寸改变时,画布自适应
1.创建适配函数,监听浏览器 resize 事件
2.调整渲染器画布宽高,摄像机宽高比和更新视椎体空间
function createHelper() {
// 1. 创建坐标轴对象,设置长度
const axesHelper = new THREE.AxesHelper(5)
// 2. 添加到场景中
scene.add(axesHelper)
}
renderResize()
7.three.js移动立方体
目的:移动目标立方体,查看效果和 three.js 组织对象的类关系
1.位移 position 属性
2.旋转 rotation 属性
3.缩放 scale 属性
function moveCube() {
// 1. 位移 position 属性(Vector3 三维向量对象)
cube.position.x = 5
// cube.position.set(5, 5, 0)
// 2. 旋转 rotation 属性(Euler 欧拉角弧度制角度对象)
在轴的正方向看是逆时针旋转
// cube.rotation.x = Math.PI / 4
// 3. 缩放 scale 属性(Vector3 三维向量对象,中心原点不动,向 2 边拉伸/缩小)
cube.scale.z = 2
}
moveCube()
8.three.js 使用 GUI 工具
目的:借助 GUI 工具实时辅助调试 3D 物体
安装:npm i dat.gui
引入:import * as dat from 'dat.gui'
使用:
1. 创建 GUI 对象
const gui = new dat.GUI()
2.添加具体控制器使用
gui.add(参数1,参数2)
参数1:关联 DOM 对象,JS 对象,3D 物体对象
参数2:对象其中的某个属性,给这个属性关联用户界面工具(从而快速调整它的值)
3.控制立方体颜色
gui.addColol()
4.创建分组-影响立方体位置
gui.addFolder()
插件文档:https://github.com/dataarts/dat.gui/blob/master/API.md
function createGUI() {
const gui = new dat.GUI()
gui.add(document, 'title')
// 1 控制立方体显示/隐藏(布尔->多选框)
gui.add(cube, 'visible')
// 2 轨道控制器回归初始角度(函数->按钮)
gui.add(controls, 'reset')
// 3 控制立方体颜色(找属性方式:文档->打印->百度)
// 效果:立方体默认颜色和文字 <=> 显示在工具标签上
const colorObj = {
'col': `#${cube.material.color.getHexString()}`
}
gui.addColor(colorObj, 'col').onChange(val => {
// val: #ff00ff 十六进制的颜色字符串
cube.material.color = new THREE.Color(val)
})
// 4 创建分组-影响立方体位置
const folder = gui.addFolder('位移')
// 参数3:最小值范围,参数4:最大值范围,参数5:步长 (数字->进度条)
folder.add(cube.position, 'x', 0, 5, 0.1)
folder.add(cube.position, 'y', 0, 5, 0.1)
folder.add(cube.position, 'z', 0, 5, 0.1)
// 5 下拉菜单(关键:第三个参数为对象时->下拉菜单)
// 对象中属性名->下拉菜单选项名
// 初始值匹配后会影响下拉菜单默认选中哪一项
gui.add({ type: '1' }, 'type', { '方案1': '1', '方案2': '2', '方案3': '3' }).onChange(val => {
// val 方案对象的 '1','2'
switch (val) {
//方案1
case '1':
cube.position.set(0, 0, 0)
break;
//方案2
case '2':
cube.position.set(2, 2, 2)
break;
//方案3
case '3':
cube.position.set(2, 0, 0)
break;
}
})
}
createGUI()
整个代码:
<template>
<div id="threeID">
</div>
</template>
<script setup>
// 引入 three 库
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
//引入dat.gui 库
import * as dat from 'dat.gui'
// 创建场景,摄像机,渲染器
let scene, camera, renderer
// 创建轨道控制器
let controls
//创建立方体
let cube
/**
* 创建基础场景
*/
function init() {
// 1. 创建场景对象
scene = new THREE.Scene()
// 2. 创建摄像机对象
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
//移动摄像机向z轴5个单位(默认摄像机和物体在原点)
camera.position.z = 5
// 3. 创建渲染器,并设置画布大小,添加到 DOM 显示
renderer = new THREE.WebGLRenderer()
// 设置画布大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 把画布 canvas 标签添加到id为threeID上
document.querySelector('#threeID').append(renderer.domElement)
}
/**
* 创建立方体
*/
function createCube() {
// 1. 创建图形,宽高深为 1 单位(立方缓冲几何体)
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 2. 创建材质,颜色为绿色 0x00ff00 (网格基础材质-线面纯颜色描绘表面)
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 3. 创建网格物体对象,传入图形和材质(网格物体对象)
cube = new THREE.Mesh(geometry, material);
// 4. 把物体加入到场景中
scene.add(cube);
}
/**
* 轨道控制器
*/
function controlsCreate() {
// 创建轨道控制器
controls = new OrbitControls(camera, renderer.domElement)
// 1. 阻尼效果
controls.enableDamping = true
// 2. 开启自动旋转轨道控制器效果->带动摄像机一起旋转(摄像机顺时针水平旋转)
// controls.autoRotate = true
// 3. 垂直角度范围控制(0 上面,Math.PI 下面)
// controls.maxPolarAngle = Math.PI
// controls.minPolarAngle = 0
// 水平角度范围控制
// controls.maxAzimuthAngle = 1.5 * Math.PI
// controls.minAzimuthAngle = 0.5 * Math.PI
// 4. 摄像机移动范围控制
// controls.minDistance = 2
// controls.maxDistance = 10
}
function renderLoop() {
// 在渲染循环中更新场景渲染
renderer.render(scene, camera)
// 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
controls.update()
// 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
// 好处:当前页面切换到后台,暂停递归
requestAnimationFrame(renderLoop)
}
/**
* 坐标轴
*/
function createHelper() {
// 1. 创建坐标轴对象,设置长度
const axesHelper = new THREE.AxesHelper(5)
// 2. 添加到场景中
scene.add(axesHelper)
}
/**
* 适配场景大小
*/
function renderResize() {
// 1. 创建适配函数,监听浏览器 resize 事件
window.addEventListener('resize', () => {
// 2. 调整渲染器画布大小,摄像机宽高比和更新视椎体空间
renderer.setSize(window.innerWidth, window.innerHeight)
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
})
}
/**
* 移动立方体
*/
function moveCube() {
// 1. 位移 position 属性(Vector3 三维向量对象)
cube.position.x = 5
// cube.position.set(5, 5, 0)
// 2. 旋转 rotation 属性(Euler 欧拉角弧度制角度对象)
// 在轴的正方向看是逆时针旋转
// cube.rotation.x = Math.PI / 4
// 3. 缩放 scale 属性(Vector3 三维向量对象,中心原点不动,向 2 边拉伸/缩小)
// cube.scale.z = 2
}
/**
* 使用GUI工具
*/
function createGUI() {
const gui = new dat.GUI()
gui.add(document, 'title')
// 1 控制立方体显示/隐藏(布尔->多选框)
gui.add(cube, 'visible')
// 2 轨道控制器回归初始角度(函数->按钮)
gui.add(controls, 'reset')
// 3 控制立方体颜色(找属性方式:文档->打印->百度)
// 效果:立方体默认颜色和文字 <=> 显示在工具标签上
const colorObj = {
'col': `#${cube.material.color.getHexString()}`
}
gui.addColor(colorObj, 'col').onChange(val => {
// val: #ff00ff 十六进制的颜色字符串
cube.material.color = new THREE.Color(val)
})
// 4 创建分组-影响立方体位置
const folder = gui.addFolder('位移')
// 参数3:最小值范围,参数4:最大值范围,参数5:步长 (数字->进度条)
folder.add(cube.position, 'x', 0, 5, 0.1)
folder.add(cube.position, 'y', 0, 5, 0.1)
folder.add(cube.position, 'z', 0, 5, 0.1)
// 5 下拉菜单(关键:第三个参数为对象时->下拉菜单)
// 对象中属性名->下拉菜单选项名
// 初始值匹配后会影响下拉菜单默认选中哪一项
gui.add({ type: '1' }, 'type', { '方案1': '1', '方案2': '2', '方案3': '3' }).onChange(val => {
// val 方案对象的 '1','2'
switch (val) {
//方案1
case '1':
cube.position.set(0, 0, 0)
break;
//方案2
case '2':
cube.position.set(2, 2, 2)
break;
//方案3
case '3':
cube.position.set(2, 0, 0)
break;
}
})
}
onMounted(() => {
init() //初始化
createCube()//创建立方体
controlsCreate()//轨道控制器
renderLoop()//循环渲染轨道控制器
createHelper()//坐标轴
renderResize()//适配场景大小
moveCube()//移动立方体
createGUI()//使用GUI工具
renderer.render(scene, camera)
})
</script>
<style scoped></style>