简介
随着浏览器功能的愈加强大,在浏览器中显示三维图像的需求逐渐增大,WebGL
技术应运而生,通过提供的接口可以实现相应功能,但是需要学习底层逻辑,包括着色器语法、数学、图形学等相关知识,上手难度比较大,要学好并不简单,Three.js
是基于WebGL
封装的一个3D引擎,用来便捷地满足浏览器下的3D需要。
开发准备
-
安装
Three.js
:可以通过CDN的方式或者npm
安装,本文安装的是0.122.0
版本,如果相关api
不符合,可以检查版本或者根据当前版本实现功能。npm i --save three@0.122.0
-
准备
HTML
文件:需要一个canvas
节点用于挂载。<canvas id="webgl"></canvas>
-
导入
Three.js
- 整体导入:
import * as THREE from 'three'
- 按需加载:
import { WebGLRenderer} from 'three'
(导入的内容不必关心,后文会介绍)
- 整体导入:
注:本文于
webpack
环境下实现,具体的搭建方式与本文内容无关不多做赘述,使用CDN方式亦可
基础知识
在Three.js
中,主要由场景(Scene)、相机(Camera)、网格(Mesh)、渲染器(Renderer)组成
场景 Scene
可以理解为用来展示的空间,所有的图形都需要被添加到场景中才会生效。
主要使用方式:
- 创建:
new THREE.Scene()
- 添加:
scene.add()
相机 Camera
虽然构建的是一个三维图像,但是显示屏的成像结果是二维的,那如何将三维图像转化为二维图像呢?其实和拍照片是类似的。相机对准中心,调整角度和焦距,按下快门,拍摄出一张照片。Three.js
中的相机也是类似的操作。
最常使用的是透视相机PerspectiveCamera
,这和现实生活的视角是一致的,同种类的还有正交相机OrthograpicCamera
,会用于一些比较特殊的场景,在此就不多做介绍了,感兴趣的同学可以自行搜索。
主要使用方式:
-
创建:
new THREE.PerspectiveCamera(fov, aspect, neer, far)
-
fov
:相机的视角大小,可观测角度范围 -
aspect
:宽高比w/h
-
near、far
:近平面和远平面的距离- Tips:尽量不要使用极端值,如
0.0000001
和9999999
等,比较容易造成z-fighting
效果,在此不做介绍
- Tips:尽量不要使用极端值,如
-
- 更新观测点:
camera.lookAt(Vector3)
其中的Vector3
是一个空间坐标 - 设置宽高比:
camera.aspect = width ``/ height
网格 Mesh
网格是放置三维模型的容器,添加一个网格并且设置几何体Geometry
与材质Material
即可创建物体。可以通过创建不同的几何体和材质实现各式各样的结构,下面介绍一个最简单的立方体创建(均使用最基础的属性创建,更多属性在此不一一列举,如果后续有用到其他属性会再做解释)。
- 创建立方体:
new THREE.BoxGeometry(1, 1, 1)
创建长宽高都为1
的立方体 - 创建材质:
new THREE.MeshBasicMaterial({ color: 0xFF0000 )
创建一个红色的材质 - 创建网格:
new Mesh(geometry, material)
渲染器 Renderer
物体、物机被创建并添加到场景中,但是内容不是一成不变的,何时渲染,渲染的尺寸大小,渲染的分辨率等属性都要靠渲染器来实现。
主要使用方式
- 创建:
new THREE.WebGLRenderer({ canvas })
其中的 canvas 是在HTML
中声明的node
节点。 - 设置渲染的尺寸:
renderer.setSize(width, height)
- 开始渲染:
renderer.render(scene, camera)
代码实现
arduino
import * as THREE from 'three'
/// Scene
const scene = new THREE.Scene()
/// Red cube
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
// 网格由几何体和材料组成
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
const sizes = { width: 800, height: 600 }
/// Camera
// 角度 + 宽高比 + 近远平面
const aspectRadio = sizes.width / sizes.height
const camera = new THREE.PerspectiveCamera(75, aspectRadio, 0.1, 100)
scene.add(camera)
/// Renderer
const canvas = document.querySelector('#webgl')
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(sizes.width, sizes.height)
renderer.render(scene, camera)
实现以上代码后,可以看到如下结果:
黑色区域就是视图渲染的效果,发现有两个明显的问题:
- 黑色区域并没有布满屏幕,效果不好
- 黑色区域没有显示内容,红色立方体去哪儿了?
先来解决第一个问题,这是因为sizes
是800
和600
,前文有提到它代表的是渲染的尺寸范围,应该将其改为屏幕宽高
arduino
const sizes = { width: window.innerWidth, height: window.innerHeight }
可以看到还有边框,不要忘了设置css
属性哦
css
body {
margin: 0;
padding: 0;
}
html,
body {
overflow: hidden;
}
这时候能看到屏幕全黑了,我们来解决第二个问题。
考虑一个问题,我们的成像是怎么做到的?用的相机。那新生成的相机和几何体的位置会在哪儿呢,一般来说是在原点的,而相机是向着 -z 轴方向看的,这显然是不合适的,做法也很简单,移动一下相机的位置即可。
ini
camera.position.z = 3
此时观测角度是从正上方往下看的,可以看到出现了一个红色正方形。
如果向上移动相机呢?我们设置相机的 y 坐标为3
观测好像出问题了,问题在于相机的朝向是 -z 轴方向,相机的位置是(0, 3, 3)
,看向的显然不是原点,此时我们要把它指回原点,有两种做法:
- 直接指向原点坐标
camera.lootAt(new THREE.Vector(0, 0, 0))
- 指向我们创建的网格物体
camera.lootAt(mesh.position)
方向变正确了!
接下来我们让它动起来。方法很简单,只需要实现一个简易的tick
方法:
scss
const tick = () => {
// 改变网格的旋转角度
mesh.rotation.x += 0.01
mesh.rotation.y += 0.01
// 每次都必须重新进行渲染操作
renderer.render(scene, camera)
// 每一帧都重新调用该方法
requestAnimationFrame(tick)
}
tick()
最终代码
arduino
import * as THREE from 'three'
/// Scene
const scene = new THREE.Scene()
/// Red cube
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
// 网格由几何体和材料组成
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
const sizes = { width: window.innerWidth, height: window.innerHeight }
/// Camera
// 角度 + 宽高比 + 近远平面
const aspectRadio = sizes.width / sizes.height
const camera = new THREE.PerspectiveCamera(75, aspectRadio, 0.1, 100)
camera.position.z = 3
scene.add(camera)
/// Renderer
const canvas = document.querySelector('#webgl')
const renderer = new THREE.WebGLRenderer({ canvas })
renderer.setSize(sizes.width, sizes.height)
const tick = () => {
mesh.rotation.x += 0.01
mesh.rotation.y += 0.01
renderer.render(scene, camera)
requestAnimationFrame(tick)
}
tick()
总结
本文介绍了Three.js中的主要框架和基础内容,从零到一实现了一个最简易立方体的显示和转动,下一篇文章会介绍简单的优化和一些辅助工具,包括自带的和使用广泛的工具。