前端可视化三件套(三) Three.js入门

1,501 阅读13分钟

文章链接:Three.js教程

一 概述

1 链接

Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使用最广泛的三维引擎。

资源链接:

github链接:github.com/mrdoob/thre…

Three.js官网:threejs.org/

Threejs中文文档

github下载threejs比较慢,所以在网盘放了一份,方便大家下载。

下载地址查看文章:Three.js-master包下载

案例:

物联网粮仓3D可视化案例:www.yanhuangxueyuan.com/3D/liangcan…

玉镯产品在线预览案例:www.yanhuangxueyuan.com/3D/liangcan…

沙发在线预览:app.xuanke3d.com/apps/trayto…

服装在线预览:suit.xuantech.cn/

洗衣机在线交互预览:cdn.weshape3d.com/hir001/1021…

解析GeoJOSN数据中国GDP数据可视化:www.yanhuangxueyuan.com/3D/geojsonC…

科研平台-蛋白质结构可视化案例:www.rcsb.org/3d-view/2JE…

化学相关——分子结构可视化:www.yanhuangxueyuan.com/3D/fenzi/in…

地理天文相关——太阳系3D预览:www.yanhuangxueyuan.com/3D/solarSys…

机械模型在线预览demo:www.yanhuangxueyuan.com/3D/jixiezhu…

室内设计作品展示案例:www.yanhuangxueyuan.com/3D/houseDes…

2 Three.js master目录结构

github链接查看所有目录:github.com/mrdoob/thre…

three.js-master
└───build——src目录下各个代码模块打包后的结果
    │───three.js——开发的时候.html文件中要引入的threejs引擎库,和引入jquery一样,可以辅助浏览器调试
    │───three.min.js——three.js压缩后的结构文件体积更小,可以部署项目的时候在.html中引入。
    │
└───docs——Three.js API文档文件
    │───index.html——打开该文件可以实现离线查看threejs API文档
    │
└───editor——Three.js的可视化编辑器,可以编辑3D场景
    │───index.html——打开应用程序
    │
└───docs——Three.js API文档文件
    │───index.html——打开该文件可以实现离线查看threejs API文档
    │
└───examples——里面有大量的threejs案例,平时可以通过代码编辑全局查找某个API、方法或属性来定位到一个案例
    │
└───src——Three.js引擎的各个模块,可以通过阅读源码深度理解threejs引擎
    │───index.html——打开该文件可以实现离线查看threejs API文档
    │
└───utils——一些辅助工具
    │───\utils\exporters\blender——blender导出threejs文件的插件

\

3 安装下载

概述第三节安装下载

注:案例中是html页面直接引入 项目中不这样用

需要npm 安装之后在使用

二、快速入门

要求:在现有代码的基础删 进行删改测试熟悉功能

1 第一个例子:

(梦开始的地方)

<!DOCTYPE html>
<html lang="en">
	
	<head>
		<meta charset="UTF-8">
		<title>第一个three.js文件_WebGL三维场景</title>
		<style>
			body {
				margin: 0;
				overflow: hidden;
				/* 隐藏body窗口区域滚动条 */
			}
		</style>
		<!--引入three.js三维引擎-->
		<script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
		<!-- <script src="./three.js"></script> -->
		<!-- <script src="http://www.yanhuangxueyuan.com/threejs/build/three.js"></script> -->
	</head>
	
	<body>
		<script>
			/**
			* 创建场景对象Scene
			*/
			var scene = new THREE.Scene();
			/**
			* 创建网格模型
			*/
			// var geometry = new THREE.SphereGeometry(60, 40, 40); //创建一个球体几何对象
			var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
			var material = new THREE.MeshLambertMaterial({
				color: 0x0000ff
			}); //材质对象Material
			var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
			scene.add(mesh); //网格模型添加到场景中
			/**
			* 光源设置
			*/
			//点光源
			var point = new THREE.PointLight(0xffffff);
			point.position.set(400, 200, 300); //点光源位置
			scene.add(point); //点光源添加到场景中
			//环境光
			var ambient = new THREE.AmbientLight(0x444444);
			scene.add(ambient);
			// console.log(scene)
			// console.log(scene.children)
			/**
			* 相机设置
			*/
			var width = window.innerWidth; //窗口宽度
			var height = window.innerHeight; //窗口高度
			var k = width / height; //窗口宽高比
			var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大
			//创建相机对象
			var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
			camera.position.set(200, 300, 200); //设置相机位置
			camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
			/**
			* 创建渲染器对象
			*/
			var renderer = new THREE.WebGLRenderer();
			renderer.setSize(width, height);//设置渲染区域尺寸
			renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
			document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
			//执行渲染操作   指定场景、相机作为参数
			renderer.render(scene, camera);
		</script>
	</body>
</html>

搭建步骤:html页面中

1 引入 three,js三维引擎

2 创建场景对象Scena THREE.Scene();

3 创建网格模型

球体:THREE.SphereGeometry(60, 40, 40);

立方体: THREE.BoxGeometry(100, 100, 100);

4 光源设置

设置光源点位置并添加,环境光

5 相机设置

窗口高度 宽度 宽高比 控制系数 相机对象 位置 相机方向

6 创建渲染器对象

尺寸 背景 canvans对象

2 名词解释

场景 Scene

几何体Geometry 三个参数是长宽高 直接使用构造函数调用就OK

var geometry = new THREE.BoxGeometry(100, 100, 100);

球体:SphereGeometry

//创建一个球体几何对象
var geometry = new THREE.SphereGeometry(60, 40, 40);

材质 Material: 显示上面几何体的材质 包括颜色等等

材质文档链接

var material=new THREE.MeshLambertMaterial({color:0x0000ff});

光源:Light

通过构造函数THREE.PointLight()创建了一个点光源对象,参数0xffffff定义的是光照强度

var point=new THREE.PointLight(0xffffff);

相机 Camera

相机Camera

通过构造函数THREE.OrthographicCamera()创建了一个正射投影相机对象

相机构造函数的的前四个参数定义的是拍照窗口大小, 就像平时拍照一样,取景范围为大,被拍的人相对背景自然变小了。

camera.position.set(200, 300, 200);和camera.lookAt(scene.position);定义的是相机的位置和拍照方向,

可以更改camera.position.set(200,300,200);参数重新定义的相机位置,把第一个参数也就是x坐标从200更改为250, 你会发现立方的在屏幕上呈现的角度变了,这就像你生活中拍照人是同一个人,但是你拍照的位置角度不同,显示的效果肯定不同。这些具体的参数细节可以不用管, 至少你知道相机可以缩放显示三维场景、对三维场景的不同角度进行取景显示。

var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);

渲染器 Randerer

流程:

在第一个案例中 使用

THREE.Scene()场景 THREE.OrthographicCamera()相机 THREE.WebGLRenderer()渲染器

三个顶层对象 然后再通过顶层对象对字对象的方法和属性进行设置

相机和渲染对象相对简单 最复杂的是场景对象

new THREE.Mesh(box,material);使用构造函数Mesh()创建了一个网格模型对象,该对象把上面两行含有顶点位置信息的几何体对象和含有颜色信息的材质对象作为参数,

网格模型创建好之后, 需要使用场景对象的方法.add()把三维场景的子对象添加到场景中,

new THREE.PointLight(0xffffff);、new THREE.AmbientLight(0x444444);定义了两个点光源、环境光对象,然后作为场景的子对象插入场景中。

场景、相机、渲染器设置完成后,设置代码renderer.render(scene,camera)把场景、相机对象作为渲染器对象方法render()的参数,这句代码的意义相当于告诉浏览器根据相机的放置方式拍摄已经创建好的三维场景对象。

场景 相机 渲染器

从WebGL的角度来看,three.js提供的构造函数基本是对原生WebGL的封装,如果你有了WebGL的基础,在学习three.js的很多对象、方法和属性是很容易理解的。在three.js入门教程中不会去过多讲解WebGL的基础知识, 但是为了大家更好的理解three.js的很多命令,与three.js相关的WebGL API知识、GPU渲染管线的知识。图形学可能很多人会觉得比较难,其实主要是算法部分,大家先可以学习一些基本的WebGL知识,初学的时候尽量不关注算法,主要了解顶点数据处理的过程,GPU渲染管线的基本功能单元。实际的工作中如果不是开发3D引擎可能不会使用原生WebGL API,但是学习了这些之后,对于three.js的深度开发学习很有好处,如果你了解你WebGL知识,可以联系绘制函数drawArrays()来理解渲染器的渲染操作方法render()。

3 旋转

周期性旋转

每执行一次渲染器对象WebGLRenderer的渲染方法.render(),浏览器就会渲染出一帧图像并显示在Web页面上,这就是说你按照一定的周期不停地调用渲染方法 .render() 就可以不停地生成新的图像覆盖原来的图像。这也就是说只要一边旋转立方体,一边执行渲染方法.render()重新渲染,就可以实现立方体的旋转效果。

周期性渲染采用的是定时器 setInterval() 每隔多少毫秒执行一次

// 渲染函数
function render() {
    renderer.render(scene,camera);//执行渲染操作
    mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
}
//间隔20ms周期性调用函数fun,20ms也就是刷新频率是50FPS(1s/20ms),每秒渲染50次
setInterval("render()",20);

上面代码定义了一个渲染函数render(),函数中定义了三个语句,通过setInterval("render()",20);可以实现m每间隔20毫秒调用一次函数render(),每次调用渲染函数的时候,执行renderer.render(scene,camera);渲染出一帧图像,执行mesh.rotateY(0.01);语句使立方体网格模型绕y轴旋转0.01弧度。

渲染频率

调用渲染方法.render()进行渲染的渲染频率不能太低,比如执行setInterval("render()",200);间隔200毫秒调用渲染函数渲染一次,相当于每秒渲染5次,你会感觉到比较卡顿。渲染频率除了不能太低,也不能太高,太高的话计算机的硬件资源跟不上,函数setInterval()设定的渲染方式也未必能够正常实现。一般调用渲染方法.render()进行渲染的渲染频率控制在每秒30~60次, 人的视觉效果都很正常,也可以兼顾渲染性能。

//设置调用render函数的周期为200ms,刷新频率相当于5你能明显的感受到卡顿
setInterval("render()",200);

函数requestAnimationFrame()

实际开发中,为了更好的利用浏览器渲染,可以使用函数requestAnimationFrame()代替setInterval()函数,requestAnimationFrame()和setInterval()一样都是浏览器window对象的方法。(那前面还说那么多废话 不想上班了想放假)

requestAnimationFrame()参数是将要被调用函数的函数名,requestAnimationFrame()调用一个函数不是立即调用而是向浏览器发起一个执行某函数的请求, 什么时候会执行由浏览器决定,一般默认保持60FPS的频率,大约每16.7ms调用一次requestAnimationFrame()方法指定的函数,60FPS是理想的情况下,如果渲染的场景比较复杂或者说硬件性能有限可能会低于这个频率。可以查看文章《requestAnimationFrame()》了解更多requestAnimationFrame()函数的知识。

function render() {
        renderer.render(scene,camera);//执行渲染操作
        mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
        requestAnimationFrame(render);//请求再次执行渲染函数render
    }
render();

均匀旋转

在实际执行程序的时候,可能requestAnimationFrame(render)请求的函数并不一定能按照理想的60FPS频率执行,两次执行渲染函数的时间间隔也不一定相同,如果执行旋转命令的rotateY的时间间隔不同,旋转运动就不均匀,为了解决这个问题需要记录两次执行绘制函数的时间间隔。

使用下面的渲染函数替换原来的渲染函数即可,rotateY()的参数是0.001t,也意味着两次调用渲染函数执行渲染操作的间隔t毫秒时间内,立方体旋转了0.001t弧度,很显然立方体的角速度是0.001弧度每毫秒(0.0001 rad/ms = 1 rad/s = 180度/s)。CPU和GPU执行一条指令时间是纳秒ns级,相比毫秒ms低了6个数量级,所以一般不用考虑渲染函数中几个计时语句占用的时间,除非你编写的是要精确到纳秒ns的级别的标准时钟程序。

let T0 = new Date();//上次时间
function render() {
        let T1 = new Date();//本次时间
        let t = T1-T0;//时间差
        T0 = T1;//把本次时间赋值给上次时间
        requestAnimationFrame(render);
        renderer.render(scene,camera);//执行渲染操作
        mesh.rotateY(0.001*t);//旋转角速度0.001弧度每毫秒
    }
render();

4 鼠标操作旋转缩放

使用控件 OrbitControls.js

位置 (three.js-master\examples\js\controls)

OrbitControls.js控件支持鼠标左中右键操作和键盘方向键操作,具体代码如下,使用下面的代码替换1.1节中renderer.render(scene,camera);即可。

function render() {
  renderer.render(scene,camera);//执行渲染操作
}
render();
var controls = new THREE.OrbitControls(camera,renderer.domElement);//创建控件对象
controls.addEventListener('change', render);//监听鼠标、键盘事件

OrbitControls.js控件提供了一个构造函数THREE.OrbitControls() 把一个相机对象作为参数的时候,执行代码new THREE.OrbitControls(camera,renderer.domElement),

浏览器会自动检测鼠标键盘的变化, 并根据鼠标和键盘的变化更新相机对象的参数,比如你拖动鼠标左键,浏览器会检测到鼠标事件,把鼠标平移的距离按照一定算法转化为相机的的旋转角度,你可以联系生活中相机拍照,即使景物没有变化,你的相机拍摄角度发生了变化,自然渲染器渲染出的结果就变化了,

通过定义监听事件controls.addEventListener('change', render),如果你连续操作鼠标,相机的参数不停的变化,同时会不停的调用渲染函数render()进行渲染,这样threejs就会使用相机新的位置或角度数据进行渲染。

执行构造函数THREE.OrbitControls()浏览器会同时干两件事,一是给浏览器定义了一个鼠标、键盘事件,自动检测鼠标键盘的变化,如果变化了就会自动更新相机的数据, 执行该构造函数同时会返回一个对象,可以给该对象添加一个监听事件,只要鼠标或键盘发生了变化,就会触发渲染函数。 关于监听函数addEventListener介绍可以关注文章《HTML5事件》

场景操作

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

注意开发中不要同时使用requestAnimationFrame()或controls.addEventListener('change', render)调用同一个函数,这样会冲突。

5 插入新的几何体

SphereGeometry构造函数

SphereGeometry(radius, widthSegments, heightSegments)

三个参数分别是球体半径 控制球面精度水平精度 垂直精度

绘制球体网格模型

使用THREE.SphereGeometry(60,40,40);替换立方体几何体代码new THREE.BoxGeometry(100, 100, 100);。

常见的几何体

//长方体 参数:长,宽,高
var geometry = new THREE.BoxGeometry(100, 100, 100);
// 球体 参数:半径60  经纬度细分数40,40
var geometry = new THREE.SphereGeometry(60, 40, 40);
// 圆柱  参数:圆柱面顶部、底部直径50,50   高度100  圆周分段数
var geometry = new THREE.CylinderGeometry( 50, 50, 100, 25 );
// 正八面体
var geometry = new THREE.OctahedronGeometry(50);
// 正十二面体
var geometry = new THREE.DodecahedronGeometry(50);
// 正二十面体
var geometry = new THREE.IcosahedronGeometry(50);

同时绘制多个几何体

这也比较简单,直接模仿立方体的代码就可以,需要创建一个几何体对象作和一个材质对象,然后把两个参数作为网格模型构造函数Mesh()的参数创建一个网格模型,然后再使用场景对象scene的方法.add()把网格模型mesh加入场景中。

threejs的几何体默认位于场景世界坐标的原点(0,0,0),所以绘制多个几何体的时候,主要它们的位置设置。

下面代码同时绘制了立方体、球体和圆柱三个几何体对应的网格模型。

// 立方体网格模型
var geometry1 = new THREE.BoxGeometry(100, 100, 100);
var material1 = new THREE.MeshLambertMaterial({
  color: 0x0000ff
}); //材质对象Material
var mesh1 = new THREE.Mesh(geometry1, material1); //网格模型对象Mesh
scene.add(mesh1); //网格模型添加到场景中

// 球体网格模型
var geometry2 = new THREE.SphereGeometry(60, 40, 40);
var material2 = new THREE.MeshLambertMaterial({
  color: 0xff00ff
});
var mesh2 = new THREE.Mesh(geometry2, material2); //网格模型对象Mesh
mesh2.translateY(120); //球体网格模型沿Y轴正方向平移120
scene.add(mesh2);

// 圆柱网格模型
var geometry3 = new THREE.CylinderGeometry(50, 50, 100, 25);
var material3 = new THREE.MeshLambertMaterial({
  color: 0xffff00
});
var mesh3 = new THREE.Mesh(geometry3, material3); //网格模型对象Mesh
// mesh3.translateX(120); //球体网格模型沿Y轴正方向平移120
mesh3.position.set(120,0,0);//设置mesh3模型对象的xyz坐标为120,0,0
scene.add(mesh3); //

辅助三维坐标系AxisHelper

为了方便调试预览threejs提供了一个辅助三维坐标系AxisHelper,可以直接调用THREE.AxisHelper创建一个三维坐标系,然后通过.add()方法插入到场景中即可。

// 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置 var axisHelper = new THREE.AxisHelper(250); scene.add(axisHelper);