可能每个人的童年都有个时空穿梭梦,印象深刻的是第一次看《机器猫》,主角康夫卧室的抽屉就是时空隧道的入口,跳进隧道的那一刻,时间浮光掠影般从身边流过,仿佛进入了异世界。
这次我们用three.js实现一个3d隧道穿梭效果,打开异世界的大门。 它也可以是一个跑酷游戏原型,又或者是3D空间导航,具体怎么应用由你来决定,先来看下效果。
无限循环的隧道实际上是一个首尾无缝相连的管道,你可以将它的形状想象成一个甜甜圈。我们只要将摄像机放置在这个管道内部,并沿着管道路径运动就能实现这种穿越效果。
实现无限循环隧道
首先要实现这样一个管道我们可以借助three.js中TubeGeometry对象,TubeGeometry能够沿着一条3D曲线创建管道。我们要做的是先创建一条曲线,曲线是用坐标函数建立的,我们先拿TubeGeometry的官方示例试一下。
CustomSinCurve.prototype.getPoint = function ( t ) {var tx = t * 3 - 1.5var ty = Math.sin( 2 * Math.PI * t )var tz = 0return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale )}
官方示例的getPoint函数绘制了一个烟斗状的曲线管道,看起来平淡无奇,别着急我们来逐步完善。
改变下getPonit函数中的公式,我们马上得到了一个封闭的圆环管道。
CustomSinCurve.prototype.getPoint = function (t) {const tx = Math.cos(2 * Math.PI * t)const ty = Math.sin(2 * Math.PI * t)const tz = 0return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale)}
为了让路径有更多变化,我们在z轴上也应用正弦函数,生成起伏状的圆环模型。
const tz = 0.1 * Math.sin(8 * Math.PI * t)
为隧道添加材质
贴图是这个效果的灵魂,准备一张1024×1024尺寸的材质贴图,它会重复地显示在管道模型表面,注意贴图中明暗交替的线条,这是实现背景光线效果的关键。
设置材质的贴图方式和重复次数,使材质沿曲线方向重复延展,我们得到了一个贴好图的隧道模型。
material.map.wrapS = THREE.RepeatWrappingmaterial.map.wrapT= THREE.RepeatWrappingmaterial.map.repeat.set(10, 1)
设置摄像机路径
要实现穿越效果,我们要将摄像机放在模型内部,并沿着隧道路径运动。官方的案例中也有一个相机沿曲线运动的例子,但经过试验效果并不理想,所以这里使用另外一种方法,这种方法考虑了隧道路径的法向量,使转弯处过渡效果更加自然。由于代码较长,就不贴在这里了,具体可以参考本篇教程的源文件。
沿路径添加物体
为了在隧道路径上添加物体,先要在管道路径上通过getPointAt方法上获得坐标点,然后将物体设置在坐标点处,这里的物体是一个个透明贴图的平面对象。
for(let i = 0; i < 20; i++) {const plane = mesh.clone()const pos = this.tubeGeometry.parameters.path.getPointAt(i * 0.04)plane.position.copy(pos)this.scene.add(plane)}
最后,将这些平面对象旋转朝向摄像机,可以通过设置四元数属性和相机相同轻松搞定。四元数是3D空间旋转常用的数学方法,可以避免使用矩阵旋转时产生的万向锁问题,由于它数学原理比较复杂,这里就不做介绍了,有兴趣可以找相关资料学习。
this.planes.forEach(plane => {plane.quaternion.copy(this.camera.quaternion)})
总结
我们已经掌握了如何使相机沿着自定义曲线运动,并实现了一个炫酷的空间隧道效果。接下来请发挥你的想象,做出更有趣的应用。
这里是一些建议:可以用three.js的postprocessing加入后期效果,使它更有科技感;可以加入鼠标交互,使摄像机根据鼠标滚轮向前移动;可以和cannon.js物理库结合,加入碰撞检测等等。
教程源文件:
/imokya/threejs-tunnel-effect
原文地址:
/threejs-infinite-tunnel/