three.js其实是使用WebGL来绘制三维效果的,它封装了诸如场景、灯光、阴影、材质、贴图、空间运算等一系列功能,让你不必要再从底层WebGL开始写起。
上图需要注意的事项:
-
首先有一个渲染器(
Renderer
)。这可以说是three.js的主要对象。你传入一个场景(Scene
)和一个摄像机(Camera
)到渲染器(Renderer
)中,然后它会将摄像机视椎体中的三维场景渲染成一个二维图片显示在画布上。 -
其次有一个场景图 它是一个树状结构,由很多对象组成,比如图中包含了一个场景(
Scene
)对象 ,多个网格(Mesh
)对象,光源(Light
)对象,群组(Group
),三维物体(Object3D
),和摄像机(Camera
)对象。一个场景(Scene
)对象定义了场景图最基本的要素,并包了含背景色和雾等属性。这些对象通过一个层级关系明确的树状结构来展示出各自的位置和方向。子对象的位置和方向总是相对于父对象而言的。比如说汽车的轮子是汽车的子对象,这样移动和定位汽车时就会自动移动轮子。注意图中摄像机(
Camera
)是一半在场景图中,一半在场景图外的。这表示在three.js中,摄像机(Camera
)和其他对象不同的是,它不一定要在场景图中才能起作用。相同的是,摄像机(Camera
)作为其他对象的子对象,同样会继承它父对象的位置和朝向。 -
网格(
Mesh
)对象可以理解为用一种特定的材质(Material
)来绘制的一个特定的几何体(Geometry
)。材质(Material
)和几何体(Geometry
)可以被多个网格(Mesh
)对象使用。比如在不同的位置画两个蓝色立方体,我们会需要两个网格(Mesh
)对象来代表每一个立方体的位置和方向。但只需一个几何体(Geometry
)来存放立方体的顶点数据,和一种材质(Material
)来定义立方体的颜色为蓝色就可以了。两个网格(Mesh
)对象都引用了相同的几何体(Geometry
)和材质(Material
)。 -
几何体(
Geometry
)对象顾名思义代表一些几何体,如球体、立方体、平面、狗、猫、人、树、建筑等物体的顶点信息。Three.js内置了许多基本几何体 。你也可以创建自定义几何体或从文件中加载几何体。 -
材质(
Material
)对象代表绘制几何体的表面属性,包括使用的颜色,和光亮程度。一个材质(Material
)可以引用一个或多个纹理(Texture
),这些纹理可以用来,打个比方,将图像包裹到几何体的表面。 -
纹理(
Texture
)对象通常表示一幅要么从文件中加载,要么在画布上生成,要么由另一个场景渲染出的图像。
下面在vue中使用three.js绘制出三个可以旋转的立方体的例子:
<template>
<div style="position:relative;width:100%;height: 100%;">
<canvas id="op"></canvas>
</div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import { onMounted } from 'vue';
onMounted(() => {
main();
})
function main() {
const canvas = document.querySelector('#op');
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas }); // 创建WEBGL渲染器
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far); // 创建摄像机对象
camera.position.z = 2;
const scene = new THREE.Scene(); // 创建场景对象
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(- 1, 2, 4);
scene.add(light); // 添加灯光效果
}
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth); // 创建一个包含盒子信的立方几何体
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({ color }); // 创建材质对象
const cube = new THREE.Mesh(geometry, material); // 创建网格对象
scene.add(cube);
cube.position.x = x;
return cube;
}
const cubes = [
makeInstance(geometry, 0x44aa88, 0),
makeInstance(geometry, 0x8844aa, - 2),
makeInstance(geometry, 0xaa8844, 2),
];
function render(time) {
time *= 0.001;
// 解决不同屏幕保持比例与清晰度问题
if (resizeRendererToDisplaySize(renderer)) {
// 通过设置相机的宽高比为canvas的宽高比解决比例适配问题
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
// 旋转动画效果
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
// 与图片相似,canvas内部分辨率又叫做绘图缓冲区(drawingbuffer)尺寸,设置canvas的绘图缓冲区与canvas显示尺寸相同解决清晰度问题
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
console.log('canvas', canvas)
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
requestAnimationFrame(render);
}
</script>
<style scoped>
#op {
display: block;
width: 100%;
height: 100%;
}
</style>