这是3D可视化教程系列的文章,如果第一次阅读请先阅读《3D可视化教程导读》

源码及3D项目文件

  源码及工程项目都放到github上。
  源码:threejs-example

效果

  光线效果是构造 “科技感” 的必备效果之一,结合光线流动构建出光线痕迹:

代码说明

  光线向外扩散的效果,来源于后期处理(Post Processing)的bloom效果,官方例子。bloom效果是根据物体上的颜色来生成,颜色偏亮则有光扩散的效果,官方例子不再多说。
  光线痕迹有两种,一种是如同流星一样整个物体移动,另一种是如同道路一样,车灯在道路上移动造成光线痕迹(light trail)。前者可以让物体移动或者使用光粒子效果。我们演示的是后者(智慧城市演示项目会用到),关键的问题有两个:

  1. 如何构建渐变色?
  2. 如何让颜色流动起来?

构建渐变色

  构建渐变色在3一般会使用alpha texture(透明度贴图)来实现。(Blender 2.83 在导出3D模型时仍然无法将 alpha texture贴图 导出来,估计后面会修复。暂时只能通过代码来补加载图片并设置。)
UV贴图透明度
  在这里我们演示如何在three.js里不使用贴图,而是通过代码给每个顶点指定颜色来实现。
首先构建曲线的geometry,设置材质颜色是根据顶点颜色来显示(THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors,} ))。
  给geometry添加一个color attribute,给每个顶点指定一个颜色:geometry.setAttribute( "color", new THREE.Float32BufferAttribute( colorArray, 3 ) );
  为了构建渐变色数组,使用了D3库的d3-interpolate(interpolate:内插),来生成两个颜色之间的渐变色。最终一个渐变色的曲线就出来了。

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
// 生成曲线
const startVec = new THREE.Vector3(500,0,0);
const controlVec = new THREE.Vector3(-400,0,20);
const endVec = new THREE.Vector3(-500,0,300);
const curve = new THREE.QuadraticBezierCurve3(startVec,controlVec,endVec);

const geometry = new THREE.TubeBufferGeometry( curve, 20, 2, 8, false );
// 使用顶点颜色 VertexColors
const material = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors,side: THREE.BackSide } );

// 生成渐变色的color数组
const count = geometry.attributes.position.count;
const rgbInterpolate = interpolateHsl("#00ffff", "#002222");
const colorArray = new Array(count);
for (let index = 0; index < count; index++) {
const t = index / count;
const rgb = rgbInterpolate(t);
const rgbValue = rgb.match(/\d+/g);
// 从 "rgb(1,2,3)" 字符串里 提取出 1,2,3 并 归一化( 0.0 ~ 1.0)
const r = Number(rgbValue[0]) / 255;
const g = Number(rgbValue[1]) / 255;
const b = Number(rgbValue[2]) / 255;

colorArray[3 * index] = r;
colorArray[3 * index + 1] = g;
colorArray[3 * index + 2] = b;
}
geometry.setAttribute( "color", new THREE.Float32BufferAttribute( colorArray, 3 ) );

const mesh = new THREE.Mesh( geometry, material);

颜色流动

  颜色流动利用TWEEN库来实现动画过程控制(把从0->1变成了 0.0,0.01,0.02……0.99,1.0的过程)。
  我们想实现绕圈效果([1,2,3]->[2,3,1]->[3,1,2]->[1,2,3],可理解为数组是环状的,不断地在绕圈子),可以把数组在对应位置拆开,然后交换位置组成新数组。(在C语言里可以通过指针来把数组头尾连起来,但在JS里好像不太好实现就暂时这样做了。)整个代码量不多,代码说明至此为止。

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
// 颜色变化
function lightMove() {
new TWEEN.Tween({value:0})
.to({value:1}, 1000)
.onUpdate(function (val) {
// 实现环状数组变化
const anchor = Number((val.value * count).toFixed(0));
const b = colorArray.slice(anchor * 3);
const f = colorArray.slice(0, anchor * 3);
const newColorArray = [].concat(b, f) ;
mesh.geometry.setAttribute( "color", new THREE.Float32BufferAttribute( newColorArray, 3 ) );
})
.start();
}

setInterval(() => {
lightMove();
}, 1000);

const animate = function () {
// TWEEN更新
TWEEN.update();
bloomComposer.render();
requestAnimationFrame( animate );

};
animate();

附录