Three.js基础入门

  目录

Three.js基础入门学习

Three.js是一款开源的主流3D绘图JS引擎(名字Three就是3D的含义),原作者为Mr.Doob。项目地址为
Three.js经常会和WebGL混淆, 但也并不总是,three.js其实是使用WebGL来绘制三维效果的。 WebGL是一个只能画点、线和三角形的非常底层的系统. 想要用WebGL来做一些实用的东西通常需要大量的代码, 这就是Three.js的用武之地。它封装了诸如场景、灯光、阴影、材质、贴图、空间运算等一系列功能,让你不必要再从底层WebGL开始写起。

场景Scene

三维场景Scene (opens new window)对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。

1
2
// 创建3D场景对象Scene
const scene = new THREE.Scene();

物体

物体 = 几何体 + 材质

几何体Geometry

img

1
2
// 创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);

材质Material

img

1
2
3
4
// 创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000, // 0xff0000设置材质颜色为红色
});

网格模型Mesh(物体)

1
2
3
4
5
6
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); // 网格模型对象Mesh
// 设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
// 在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()方法,把网格模型mesh添加到三维场景scene中。
scene.add(mesh);

摄像机Camera

Threejs如果想把三维场景Scene渲染到web网页上,还需要定义一个虚拟相机Camera,就像你生活中想获得一张照片,需要一台用来拍照的相机。
img
Threejs提供了正投影相机OrthographicCamera和透视投影相机PerspectiveCamera,本次使用比较常用的透视投影相机PerspectiveCamera。

透视投影相机PerspectiveCamera

1
2
3
4
5
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);

PerspectiveCamera( fov, aspect, near, far )

参数 含义 默认值
fov 相机视锥体竖直方向视野角度 50
aspect 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height 1
near 相机视锥体近裁截面相对相机距离 0.1
far 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 2000

相机位置.position

生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。
比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
相机对象Camera具有位置属性.position,通过位置属性.position可以设置相机的位置。

1
2
3
// 相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);

相机观察目标.lookAt()

你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标。对于threejs相机而言,就是设置.lookAt()方法的参数,指定一个3D坐标。

1
2
3
4
// 相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); // 坐标原点
camera.lookAt(0, 10, 0); // y轴上位置10
camera.lookAt(mesh.position); // 指向mesh对应的位置

渲染器WebGLRenderer

创建渲染器

通过WebGL渲染器WebGLRenderer可以实例化一个WebGL渲染器对象。

1
2
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();

设置Canvas画布尺寸.setSize()

1
2
3
4
// 定义threejs输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
renderer.setSize(width, height); // 设置three.js渲染区域的尺寸(像素px)

渲染器渲染方法.render()

渲染器WebGLRenderer执行渲染方法.render()就可以生成一个Canvas画布(照片),并把三维场景Scene呈现在canvas画布上面,你可以把.render()理解为相机的拍照动作“咔”。

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

渲染器Canvas画布属性.domElement

渲染器WebGLRenderer通过属性.domElement可以获得渲染方法.render()生成的Canvas画布,.domElement本质上就是一个HTML元素:Canvas画布。

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

光Light

实际生活中物体表面的明暗效果是会受到光照的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,咱们用网格模型Mesh模拟生活中物体,所以threejs中模拟光照Light对物体表面的影响,就是模拟光照Light对网格模型Mesh表面的影响。

受光照影响材质

threejs提供的网格材质,有的受光照影响,有的不受光照影响。
img

光源简介

Three.js提供了多种模拟生活中光源。
img
下面用点光源举例子

点光源

点光源PointLight可以类比为一个发光点,就像生活中一个灯泡以灯泡为中心向四周发射光线。

1
2
3
4
// 点光源:两个参数分别表示光源颜色和光照强度
// 参数1:0xffffff是纯白光,表示光源颜色
// 参数2:1.0,表示光照强度,可以根据需要调整
const pointLight = new THREE.PointLight(0xffffff, 1.0);

除了通过THREE.PointLight的参数2设置光照强度,你可以可以直接访问光照强度属性.intensity设置。

1
pointLight.intensity = 1.0;//光照强度

光源衰减

实际生活中点光源,比如一个灯泡,随着距离的改变,光线会衰减,越来越弱,光源衰减属性.decay默认值是2.0,如果你不希望衰减可以设置为0.0。

1
pointLight.decay = 0.0; // 设置光源不随距离衰减

光源位置

你把点光源想象为一个电灯泡,你在3D空间中,放的位置不同,模型的渲染效果就不一样。

1
2
//点光源位置
pointLight.position.set(400, 0, 0); // 点光源放在x轴上

光源添加到场景

光源和网格模型Mesh对应一样是三维场景的一部分,自然需要添加到三维场景中才能起作用。

1
scene.add(pointLight); // 点光源添加到场景中

动画

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

threejs旋转动画

1
2
3
4
5
6
7
// 渲染函数
function render() {
renderer.render(scene, camera); // 执行渲染操作
mesh.rotateY(0.01); // 每次绕y轴旋转0.01弧度
requestAnimationFrame(render); // 请求再次执行渲染函数render,渲染下一帧
}
render();

计算两帧渲染时间间隔和帧率

1
2
3
4
5
6
7
8
9
10
11
// 渲染循环
const clock = new THREE.Clock();
function render() {
const spt = clock.getDelta()*1000;//毫秒
console.log('两帧渲染时间间隔(毫秒)',spt);
console.log('帧率FPS',1000/spt);
renderer.render(scene, camera); //执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();

渲染循环和相机控件OrbitControls

设置了渲染循环,相机控件OrbitControls就不用再通过事件change执行renderer.render(scene, camera);,毕竟渲染循环一直在执行renderer.render(scene, camera)。

以上内容就是Three.js的基础内容,下面介绍一些辅助开发的插件。

三维坐标系

辅助观察坐标系

THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。

1
2
3
// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

AxesHelper的xyz轴

three.js坐标轴颜色红R、绿G、蓝B分别对应坐标系的x、y、z轴,对于three.js的3D坐标系默认y轴朝上。

相机控件OrbitControls

平时开发调试代码,或者展示模型的时候,可以通过相机控件OrbitControls实现旋转缩放预览效果。

OrbitControls使用

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键

引入扩展库OrbitControls.js

1
2
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

使用OrbitControls

1
2
3
4
5
6
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件

stats查看threejs渲染帧率

three.js每执行WebGL渲染器.render()方法一次,就在canvas画布上得到一帧图像,不停地周期性执行.render()方法就可以更新canvas画布内容,一般场景越复杂往往渲染性能越低,也就是每秒钟执行.render()的次数越低。
通过stats.js库可以查看three.js当前的渲染性能,具体说就是计算three.js的渲染帧率(FPS),所谓渲染帧率(FPS),简单说就是three.js每秒钟完成的渲染次数,一般渲染达到每秒钟60次为最佳状态。

引入Stats

1
2
//引入性能监视器stats.js
import Stats from 'three/addons/libs/stats.module.js';

Stats使用

1
2
3
4
5
6
7
8
9
10
11
12
//创建stats对象
const stats = new Stats();
// stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);
// 渲染函数
function render() {
// requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
stats.update();
renderer.render(scene, camera); // 执行渲染操作
requestAnimationFrame(render); // 请求再次执行渲染函数render,渲染下一帧
}
render();

demo source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//引入性能监视器stats.js
import Stats from 'three/addons/libs/stats.module.js';

// 获取屏幕宽高
const width = screen.width;
const height = screen.height;
// 变量声明
let scene, camera, mesh, renderer, directionalLight, stats;
// 初始化场景函数
function initScene() {
// 创建3D场景对象Scene
scene = new THREE.Scene();
}
// 创建物体
function initObject() {
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(20, 20, 20);
//创建一个材质对象Material
const material = new THREE.MeshLambertMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
transparent:true,//开启透明
opacity:0.8,//设置透明度
});
// 两个参数分别为几何体geometry、材质material
mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,0,0);
scene.add(mesh);
}
// 初始化相机
function initCamera() {
// 实例化一个透视投影相机对象
camera = new THREE.PerspectiveCamera(30, width/height, 1, 1000);
// 相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); // 坐标原点
}
// 初始化灯光
function initLight() {
// 环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// 平行光
directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
directionalLight.position.set(80, 50, 50);
// 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh;
scene.add(directionalLight);
}
// 初始化渲染器
function initRender() {
// 创建渲染器对象
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera); // 执行渲染操作
document.querySelector('#app').appendChild(renderer.domElement);
}
// 初始化辅助开发工具
function initHelper() {
//创建stats对象
stats = new Stats();
//stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);

// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5,0xff0000);
scene.add(dirLightHelper);

// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
// controls.addEventListener('change', function () {
// renderer.render(scene, camera); //执行渲染操作
// });//监听鼠标、键盘事件
}
// 动画
function animate() {
mesh.rotateX(0.01);
mesh.rotateY(0.01);
stats.update();
renderer.render(scene, camera); //执行渲染操作
requestAnimationFrame(animate);
}
// 开始Threejs
function start() {
initScene();
initObject();
initCamera();
initLight();
initRender();
initHelper();
animate();
}
start();