基于three.js开发可视化应用经验总结

背景简介

越来越多的应用已经不再满足于二维的数据可视化,追求效果更好的3D数据可视化去打造“数字孪生”的虚实动态结合。而3D可视化页面则也能够在更多维度去管理、展示数据,让用户直观去认知数据,实现更有效的决策管理。前端涉及到3d页面的开发,这里结合之前项目的3d页面开发实践经验,分享一些应用过程中的实现方案及疑难问题的解决思路,希望对其他前端同事提供一些帮助。

技术方案介绍及对比

Webgl

WebGL (Web图形库) 是一种JavaScript API,用于在任何兼容的Web浏览器中呈现交互式3D和2D图形,而无需使用插件。WebGL通过引入一个与OpenGL ES 2.0紧密相符合的API,可以在HTML5 元素中使用。(MDN简介)
在我的理解,WebGL给我们提供了一系列的图形接口,能够让我们通过js去使用GPU来进行浏览器图形渲染的工具。

Three.js

Three.js是一款webGL框架,由于其易用性被广泛应用。Three.js在WebGL的api接口基础上,又进行的一层封装。Three.js以简单、直观的方式封装了3D图形编程中常用的对象。Three.js在开发中使用了很多图形引擎的高级技巧,极大地提高了性能。另外,由于内置了很多常用对象和极易上手的工具,Three.js的功能也非常强大。Three.js作为WebGL框架中的佼佼者,由于它的易用性和扩展性,使得它能够满足大部分的开发需求,Three.js的具体功能如下:

  1. Three.js掩盖了3D渲染的细节:Three.js将WebGL原生API的细节抽象化,将3D场景拆解为网格、材质和光源(即它内置了图形编程常用的一些对象种类。
  2. 面向对象:开发者可以使用上层的JavaScript对象,而不是仅仅调用JavaScript函数。
    功能非常丰富:Three.js除了封装了WebGL原始API之外,Three.js还包含了许多实用的内置对象,可以方便地应用于游戏开发、动画制作、幻灯片制作、髙分辨率模型和一些特殊的视觉效果制作。
  3. 速度很快:Three.js采用了3D图形最佳实践来保证在不失可用性的前提下,保持极高的性能。
  4. 支持交互:WebGL本身并不提供拾取(picking)功能(即是否知道鼠标正处于某个物体上)。而Three.js则固化了拾取支持,这就使得你可以轻松为你的应用添加交互功能。
  5. 包含数学库:Three.js拥有一个强大易用的数学库,你可以在其中进行矩阵、投影和矢量运算。
  6. 内置文件格式支持:你可以使用流行的3D建模软件导出文本格式的文件,然后使用Three.js加载;也可以使用Three.js自己的JSON格式或二进制格式。
  7. 扩展性很强:为Three.js添加新的特性或进行自定义优化是很容易的事情。如果你需要某个特殊的数据结构,那么只需要封装到Three.js即可。
  8. 支持HTML5 canvas:Three.js不但支持WebGL,而且还支持使用Canvas2D、Css3D和SVG进行渲染。在未兼容WebGL的环境中可以回退到其它的解决方案。

WEBGL和Three.js的关系

WebGL原生的api是一种非常低级的接口,而且还需要一些数学和图形学的相关技术。对于没有相关基础的人来说,入门难度较大,Three.js将入门的门槛降低了整整的一大截,对WebGL进行封装,简化我们创建三维动画场景的过程。
用最简单的一句话概括:WebGL和Three.js的关系,相当于JavaScript和Jquery的关系。

具体应用过程

在这里插入图片描述

星空背景

Three中有两种方式实现给3d场景添加背景:

  • 设置scene.background属性,但是使用这种方法,其实设置的是个2d背景,画布上的背景不会随着相机的移动而产生跟随变化,背景效果不够真实。

  • 更多的情况下,我们需要使用一个3D环境背景,也就是所谓的天空盒(Skybox)。
    skybox其实就是一个box模型(这个box一般可以是cube或者sphere),把sky图像绘制在上面。我们把相机放在box里面,这样看起来就像是在一个环境背景中。

    实现skybox最常见的方法是制作一个立方体,对其应用纹理,从内部绘制它。在立方体的每一侧放置一个纹理(使用纹理坐标),看起来像地平线上的某个图像。使用天空球体(sky sphere)或天空圆顶(sky dome)也是比较常见的。只需创建一个立方体或球体,应用一个纹理,将其标记为THREE.BackSide,这样我们渲染的是内部而不是外部,然后直接或类似地将其放置在场景中,或者,创建两个场景,一个特殊的场景来绘制skybox/sphere/dome,另一个普通的场景来绘制其他所有内容。使用普通的远景相机来绘制,不需要正交照相机。

    实现天空盒的另外一个方法是使用Cubemap,立方体贴图是一种特殊的纹理,它有六个面。它不使用标准纹理坐标,而是使用从中心向外的方向来决定如何获得颜色。要使用它们,我们使用THREE.CubeTextureLoader()加载它们,然后将其用作场景的背景。

这里根据视觉提供的单张宽高为2:1的背景图,采用了天空球体实现3d背景效果,该方法无论是对于视觉切图输出还是前端代码编写都更为简洁,代码如下:

let texture = this.getTexture("sky-bg.png");
let backgroundBall = new Three.Mesh(
	new Three.SphereGeometry(800, 128, 128),
	new Three.MeshBasicMaterial({
		map: texture,
	})
);
backgroundBall.geometry.scale(-1, 1, 1);
this.scene.add(backgroundBall);
getTexture(picName) {
   
    let texture = this.textureMap.get(picName);
    if (!texture) {
   
        texture = new Three.TextureLoader().load(
            require(`../assets/images/three-dimension/${
     picName}`)
        );
        this.textureMap.set(picName, texture);
    }
    return texture;
},

流线汇聚效果

在这里插入图片描述

页面中存在上图所示流线效果,需要生成若干条从一个平面高度的某一半径的圆环上出发并汇聚到另一平面高度的某一小半径的圆环上的虚线簇,并沿着曲线路径形成光点跑动效果。

如图所示的曲线需要借助三次贝塞尔曲线函数生成。为保持曲线的错乱感,使得不同角度上的曲线有略微的区别,小范围内随机一个下层半径和一个上层半径;随机一个0到2π之间的角度theta,由此可以得到曲线的起点和终点数据,再在此基础上加入两个控制点,就可以得到一个三次贝塞尔曲线,根据页面效果对两个控制点数据进行调整验证即可。

//初始化行政分区圆台到市局图标的飞线和跑动光点
initFlyLines() {
   
	const group = new Three.Group();
	const lineMaterial = new Three.LineDashedMaterial({
   
		color: 0x00ff7f,
		side: Three.DoubleSide,
		dashSize: 1,
		gapSize: 1,
		transparent: true,
		opacity: 0.6,
	});
	const startMinRadius = RADIUS.LEVEL_2_RADIUS - 60;
	const startMaxRadius = RADIUS.LEVEL_2_RADIUS - 30;
	const endMinRadius = RADIUS.LEVEL_1_RADIUS / 2;
	const endMaxRadius = RADIUS.LEVEL_1_RADIUS;
	for (let i = 0; i < 50; i++) {
   
		const startRadius = this.getRandomInRange(
			startMinRadius,
			startMaxRadius
		);
		const endRadius = this.getRandomInRange(
			endMinRadius,
			endMaxRadius
		);
		const theta = Math.random() * Math.PI * 2;
		const curve = new Three.CubicBezierCurve3(
			new Three.Vector3(
				startRadius * Math.sin(theta),
				0,
				startRadius * Math.cos(theta)
			),
			new Three.Vector3(
				startRadius * Math.sin(theta),
				80,
				startRadius * Math.cos(theta)
			),
			new Three.Vector3(
				endRadius * Math.sin(theta),
				HEIGHT.LEVEL_1_HEIGHT - 70,
				endRadius * Math.cos(theta)
			),
			new Three.Vector3(
				endRadius * Math.sin(theta),
				HEIGHT.LEVEL_1_HEIGHT,
				endRadius * Math.cos(theta)
			)
		);
		const points = curve.getPoints(50);
		const lineGeo = new Three.Geometry();
		lineGeo.vertices = points;
		const line = new Three.Line(lineGeo, lineMaterial);
		line.computeLineDistances();
		const lightPoint = this.getSprite("light-point.png");
		lightPoint.scale.set(3, 3);
		lightPoint.position.set(
			startRadius * Math.sin(theta),
			0,
			startRadius * Math.cos(theta)
		);
		this
  • 6
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
实现箭头流动效果可以通过three.js中的几何体和材质来完成,具体流程如下: 1. 创建箭头的几何体 箭头可以用CylinderGeometry和ConeGeometry两个几何体组合而成,其中CylinderGeometry用于表示箭头的身体,而ConeGeometry用于表示箭头的头部。在创建几何体时,需要设置几何体的大小和位置。 2. 创建箭头的材质 箭头的材质可以使用MeshBasicMaterial或MeshLambertMaterial,这里使用MeshBasicMaterial。在创建材质时,需要设置材质的颜色和透明度。 3. 将几何体和材质组合成Mesh对象 使用THREE.Mesh()函数将箭头的几何体和材质组合成Mesh对象,并设置其位置和旋转角度。 4. 添加箭头到场景中 使用THREE.Scene.add()函数将箭头添加到场景中。 5. 定义箭头的运动路径 箭头的运动路径可以使用THREE.Curve()函数定义,可以使用贝塞尔曲线或其他曲线来定义。 6. 定义箭头的运动动画 使用THREE.AnimationMixer()函数和THREE.AnimationAction()函数来定义箭头的运动动画,其中AnimationMixer用于管理动画,AnimationAction用于定义动画的播放方式。 7. 播放箭头的运动动画 使用AnimationAction.play()函数来播放箭头的运动动画,可以设置动画的循环次数和播放速度。 8. 渲染场景 使用THREE.WebGLRenderer()函数和THREE.Renderer.render()函数来渲染场景。 完整的代码示例: ``` // 创建场景 var scene = new THREE.Scene(); // 创建相机 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 10); // 创建渲染器 var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建箭头的几何体 var cylinderGeometry = new THREE.CylinderGeometry(0.1, 0.1, 4, 32); var coneGeometry = new THREE.ConeGeometry(0.2, 1, 32); coneGeometry.translate(0, 2, 0); var arrowGeometry = new THREE.Geometry(); arrowGeometry.merge(cylinderGeometry); arrowGeometry.merge(coneGeometry); // 创建箭头的材质 var arrowMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.5 }); // 将几何体和材质组合成Mesh对象 var arrow = new THREE.Mesh(arrowGeometry, arrowMaterial); arrow.position.set(-5, 0, 0); arrow.rotation.y = Math.PI / 2; // 将箭头添加到场景中 scene.add(arrow); // 定义箭头的运动路径 var path = new THREE.Curve(); path.points = [new THREE.Vector3(-5, 0, 0), new THREE.Vector3(5, 0, 0)]; // 定义箭头的运动动画 var mixer = new THREE.AnimationMixer(arrow); var action = mixer.clipAction(new THREE.AnimationClip("move", -1, [new THREE.VectorKeyframeTrack(".position", [0, 1], [0, 0, 0, 5, 0, 0])])); action.loop = THREE.LoopOnce; action.timeScale = 2; // 播放箭头的运动动画 action.play(); // 渲染场景 function render() { requestAnimationFrame(render); mixer.update(0.01); renderer.render(scene, camera); } render(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值