0 背景
WebGL是一种3D绘图协议,其允许JavaScript和OpenGL ES2.0结合在一起,为H5 Canvas提供硬件3D加速渲染,可以借助系统显卡在浏览器里更流畅地显示3D场景和模型。Threejs是一款webGL框架,由于其易用性被广泛应用。Threejs在WebGL的api接口基础上,又进行了一层封装。
WebGL原生的api是一种非常低层的接口,需要一些数学和图形学的相关技术。其解决是如何在画布上画图的问题,怎么画点、线、面,怎么上色,怎么贴图,怎么处理光线,视角转动之后怎么换算绘制等等。对于没有相关基础的人来说,入门很难,Three.js将入门的门槛降低了一大截,其解决底层的渲染细节和复杂的数据结构,将复杂的底层细节抽象出来,简化我们创建三维动画场景的过程。
为快速入手,在使用threejs之前,需要了解场景、照相机、对象、光、渲染器等核心概念。
场景是所有物体的容器,对应着现实生活中三维世界,所有的可视化对象及相关的动作均发生在场景中。
Camera是三维世界中观察者,类似与眼睛。为了观察这个世界,需要描述空间中的位置,three.js采用右手坐标系。
Threejs中的Camera有两种,分别是正交投影相机THREE.OrthographicCamera和透视投影相机THREE.PerspectiveCamera。
正交投影与透视投影的区别如上图所示,左图是正交投影,物体发出的光平行地投射到屏幕上,远近的方块都是一样大的;右图是透视投影,近大远小,符合我们平时看东西的感觉。
对象则是场景中被观察的物体,Threejs中供显示的物体有很多,它们都继承自Object3D类,主要的对象有两种Mesh和Points。
我们都知道,计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。线段很多时,看起来就是一条平滑的弧线了。计算机中的三维模型也是类似的,普遍的做法是用三角形组成的网格来描述,我们把这种模型称之为Mesh模型。
这是著名的斯坦福兔子,随着三角形数量的增加,它的表面越来越平滑准确。
在Three中,Mesh的构造函数是这样的:Mesh( geometry, material )。geometry是它的形状,material是它的材质。不止是Mesh,创建很多物体都要用到这两个属性。下面我们来看看这两个重要的属性。
Geometry--形状,它通过存储模型用到的点集和点间关系(哪些点构成一个三角形)来达到描述物体形状的目的。Three提供了立方体(其实是长方体)、平面(其实是长方形)、球体、圆形、圆柱、圆台等6种基本形状;你也可以通过自己定义每个点的位置来构造形状;对于比较复杂的形状,我们还可以通过外部的模型文件导入。
Material--材质,材质其实是物体表面除了形状以为所有可视属性的集合,例如色彩、纹理、光滑度、透明度、反射率、折射率、发光度。Threejs里需要知道材质(Material)、贴图(Map)和纹理(Texture)的关系。材质包括了贴图以及其它。贴图其实是‘贴’和‘图’,它包括了图片和图片应当贴到什么位置。纹理其实就是‘图’。对于复杂的材质,可以通过Threejs提供的贴图和纹理api实现。同时,Threejs提供了多种材质可供选择,能够自由地选择漫反射/镜面反射等材质。
Points是另一种对象,其实就是一堆点的集合,它在之前很长时间都被称为ParticleSystem(粒子系统),而Three中的Points简单得多。因此最终这个类被命名为Points。
1.4 Light-光
同现实世界一样,我们要看到物体需要光,光影效果是让画面丰富的重要因素。Three提供了包括环境光AmbientLight、点光源PointLight、 聚光灯SpotLight、方向光DirectionalLight、半球光HemisphereLight等多种光源。只要在场景中添加需要的光源,即可实现相应得光效果。
在场景中建立了各种物体,也有了光,还有观察物体的相机,Renderer则负责将物体渲染到场景中。Renderer绑定一个canvas对象,并可以设置大小,默认背景颜色等属性。
调用Renderer的render函数,传入scene和camera,就可以把图像渲染到canvas中了。
2.1 初始化场景和相机
通过new THREE.Scene()实例化一个场景,new THREE.PerspectiveCamera()实例化透视相机,并设置相机位置和观察方向。
//初始化场景
this.scene = new THREE.Scene()
//初始化相机
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 5000)
this.camera.position.set(330, 330, 330)
this.camera.lookAt(this.scene.position)
new THREE.WebGLRenderer()实例化渲染器,参数canvas为绑定的标签为canvas的dom元素。
//初始化渲染器,绑定导canvas的dom元素上
this.renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: document.querySelector('canvas')
});
//设置渲染器的大小及背景设置
this.renderer.setSize(this.width, this.height)
this.renderer.setClearColor(this.config.background)
this.renderer.shadowMap.enabled = true //是否有阴影
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
buildLightSystem()函数实现平行光的创建及添加到初始化的场景中,包括平行光的位置,及照射方向等。
buildLightSystem() {
//平行光
let directionalLight = new THREE.DirectionalLight(0xffffff, 1.1);
directionalLight.position.set(300, 1000, 500);
directionalLight.target.position.set(0, 0, 0);
directionalLight.castShadow = true;
let d = 300;
directionalLight.shadow.camera = new THREE.OrthographicCamera(-d, d, d, -d, 500, 1600)
directionalLight.shadow.bias = 0.0001;
directionalLight.shadow.mapSize.width = directionalLight.shadow.mapSize.height = 1024;
//将光添加到场景中
this.scene.add(directionalLight)
},
Buildbuilding()函数实现场景中核心对象的创建及添加场景功能。addPlane()创建地面,addFense()创建围墙。
addPlane()函数中new THREE.BoxBufferGeometry(320,6,320)创建x=320,y=6,z=320的长方体,utils.makeMesh()工具类为该长方体对象增加表面材质。plane.positon.y设置长方体对象在场景中y轴的位置。this.scene.add(plane)增加该长方体面板到场景中。
Buildbuilding(){
//添加地板
this.addPlane();
//添加围墙
this.addFense();
},
function addPlane(){
let planeGeometry = new THREE.BoxBufferGeometry(320, 6, 320)
let plane = utils.makeMesh('lambert', planeGeometry, 0x6f5f6a)
plane.position.y = -3
this.scene.add(plane)
}
addFense()函数实现不规则3维围墙,fenseCoords定义了二维围墙各个拐点的坐标集合,makeShape方法通过拐点集合划线,实现由点到面的二维不规则平面。makeExtrudeGeometry方法将二维图形拉伸为3为图形,拉伸高度为第二个参数所决定。同样由makeMesh函数实现物体材质的实现并最后添加到场景中。
function addFense() {
//12条边,12个点,首尾点相同,转一圈。
let fenseCoords = [
[-130, -130],
[-130, 130],
[130, 130],
[130, -130],
[20, -130],
[20, -120],
[120, -120],
[120, 120],
[-120, 120],
[-120, -120],
[-20, -120],
[-20, -130],
[-130, -130]
]
let fenseShape = utils.makeShape(fenseCoords)
//将二维图形拉伸为三维图形,高度为3
let fenseGeometry = utils.makeExtrudeGeometry(fenseShape, 3)
let fense = utils.makeMesh('lambert', fenseGeometry, 0xe5cabf)
_this.scene.add(fense)
}
浏览器dom渲染时,依次执行上述函数,实现初始化场景、相机、渲染器、光系统、物体构建及挂载渲染器上,实现整个三维物体的可视化。
mounted() {
//初始化场景
this.scene = new THREE.Scene()
//初始化相机
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 5000)
this.camera.position.set(330, 330, 330)
this.camera.lookAt(this.scene.position)
//初始化渲染器,绑定导canvas的dom元素上
this.renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: document.querySelector('canvas')
});
this.renderer.setSize(this.width, this.height)
this.renderer.setClearColor(this.config.background)
this.renderer.shadowMap.enabled = true //是否有阴影
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
//构建光系统
this.buildLightSystem()
//构建建筑
this.buildbuilding()
//场景、相机挂载到渲染器上
this.renderer.render(this.scene, this.camera)
}