当前位置: 首页 > 科技观察

Three.js的3D粒子动画:星星送祝福

时间:2023-03-14 18:23:01 科技观察

“粒子动画”是大家可能经常听到的一个名词,那么什么是粒子动画呢?粒子是指物体的最小单位,例如原子和分子。在2D中,这个最小单位是一个像素,而在3D中,这个最小单位是一个顶点。粒子动画不是指物体本身的动画,而是指这些基本单元的动画。因为是组成物体的单位的动画,所以会有打散重组的效果。本篇学习3D粒子动画制作星星加持效果:思路分析在3D世界中,一个物体是由顶点组成的,3个顶点组成一个三角形,然后在三角形上粘贴不同的贴图,即一个3D模型。也就是说,3D模型是由顶点确定的几何体(Geometry),贴上不同的贴图(Material)构成的物体(Mesh等)。之后,将3D物体添加到场景(Scene)中,设置一个摄像机(Camera)角度进行观察,然后使用渲染器(Renderer)逐帧渲染。这就是3D渲染过程。3D物体是由顶点组成的,所以移动这些顶点就是粒子动画,因为基本粒子在移动的时候,自然会产生打散重组的效果。在“星辰送福”效果中,我们将星辰打散重组为“福”字。事实上,星星的顶点移动到了“福”字的顶点,从一个三维物体变成了另一个三维物体。那么星星的顶点从何而来呢?“福”字的顶点是怎么来的?星星的顶点实际上是在不同位置随机生成的点。在这些点上贴上星图就是星图效果。角色Fu的顶点是一个加载的3D模型,解析得到它的顶点数据。有了两个3D物体的顶点数据,即动画的起始坐标和结束坐标,通过不断修改每个顶点的x、y、z属性就可以实现粒子动画。这里不计算x、y、z属性值的变化,而是使用一些动画库来计算。支持加减速等时间功能。Three.js的动画库是Tween.js。简而言之,3D粒子动画就是顶点的x,y,z属性的变化,会使用动画库来计算中间属性值。从一个物体的顶点位置移动到另一个物体的顶点位置,会产生打散重组的效果,这也是粒子动画的魅力所在。思路清晰了,接下来我们来详细写代码。代码实现上面提到,3D渲染需要一个场景(Scene)来管理所有的3D对象,一个摄像机(Camera)来从不同的角度观察,还有一个渲染器(Renderer)来逐帧渲染。这部分是基础代码,先写这部分:创建场景:constscene=newTHREE.Scene();创建一个摄像头:constwidth=window.innerWidth;constheight=window.innerHeight;constcamera=newTHREE.PerspectiveCamera(45,宽度/高度,0.1,1000);相机分为透视相机和平行相机。我们这里使用的透视相机就是近大远小的透视效果。需要指定三个参数:视角(45)、纵横比(宽度/高度)和远近范围(0.1到1000)。调整相机的位置和观察方向:camera.position.set(100,0,400);camera.lookAt(场景。位置);然后是渲染器:constrenderer=newTHREE.WebGLRenderer();renderer.setSize(width,height);document.body.appendChild(renderer.domElement);渲染器需要通过requestAnimationFrame逐帧渲染:functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();准备工作完成,接下来就是绘制两个3D对象星空和祝福字符,并实现粒子动画。绘制星空星空并不是立方体、圆柱体等规则的几何体,而是由一些随机的顶点组成的。这个任意几何是使用缓冲区几何BufferGeometry创建的。为什么把这种由任意顶点组成的几何体称为缓冲区几何体呢?因为顶点在被GPU渲染之前就放在bufferbuffer中,所以这种指定一堆顶点的几何图形被称为BufferGeometry。我们创建30000个随机顶点:constvertices=[];for(leti=0;i<30000;i++){constx=THREE.MathUtils.randFloatSpread(2000);consty=THREE.MathUtils.randFloatSpread(2000);constz=THREE。MathUtils.randFloatSpread(2000);vertices.push(x,y,z);}这里使用Three.js提供的工具MathUtils生成0到2000的随机值。然后用这些顶点创建一个BufferGeometry:constgeometry=newTHREE.BufferGeometry();geometry.setAttribute('position',newTHREE.Float32BufferAttribute(vertices,3));为BufferGeometry对象设置顶点位置,指定3个值(x,y,z)为一个坐标。然后在这些顶点上创建材质(Material),也就是星星的贴图:conststar=newTHREE.TextureLoader().load('img/star.png');constmaterial=newTHREE.PointsMaterial({size:10,map:星星});有了顶点和材质,就可以创建3D对象(这里的3D对象是Points)。constpoints=newTHREE.Points(几何,材料);场景。添加(点);看渲染效果:静态没有3D感,我们让每一帧旋转,改变渲染逻辑:functionrender(){renderer.render(scene,camera);scene.rotation.y+=0.001;requestAnimationFrame(render);}再来看看:3D星空的感觉!接下来我们来做粒子动画:3D粒子动画3D粒子动画是顶点的动画,也就是x,y,z的变化。我们先实现一个最简单的效果,让星星移动到0、0、0的位置:起点坐标就是星星原来的位置,可以通过getAttribute('position')获取。动画过程使用tween.js计算:conststartPositions=geometry.getAttribute('position');for(leti=0;i{startPositions.needsUpdate=true;});tween.start();}每个点都有x,y,z坐标,即下标为i3,i3+1,i*3+2个值,我们指定从星星的初始位置移动到0,0,0的位置。然后时间函数指定为加速度(Easing.Exponential.In),而动画在3000毫秒后开始。每帧渲染完成后调用Tween.update计算最新值:functionrender(){TWEEN.update();renderer.render(scene,camera);scene.rotation.y+=0.001;requestAnimationFrame(render);}每一帧在绘制时都会调用onUpdate回调函数。我们在回调函数中将positions的needsUpdate设置为true,意思是告诉tween.js在渲染前更新到这一帧的新值。第一个粒子动画完成!来看看效果(我把这个效果称为万向天音):所有的星星粒子都集中在一个点上,这是典型的粒子动画的碎片化重组感。接下来,只要把粒子移动到“福”字的顶点,我们就会有“星辰送福”的效果。符字模型的顶点一定不能乱,自己画也不现实。这种模型一般是在建模软件中绘制出来,然后导入Three.js进行渲染。我找到了这样一个福字的3D模型:模型是fbx格式,使用FBXLoader加载:constloader=newTHREE.FBXLoader();loader.load('./obj/fu.fbx',function(object){constdestPosition=object.children[0].geometry.getAttribute('位置');});回调参数是从fbx模型加载的3D对象,是一个Group(多个3D对象的集合),取出第0个元素的geometry属性,就是对应的geometry。这样,我们就得到了目标的顶点位置。只需将粒子动画的结束位置更改为角色Fu的顶点即可:constcur=i%destPosition.count;tween.to({[i*3]:destPosition.array[cur*3],[i*3+1]:destPosition.array[(cur*3+1)],[i*3+2]:destPosition.array[(cur*3+2)]},3000*Math.random());如果startvertexposition比较多,超出的部分是从0的位置开始的,所以取余。你完成了!这就是我们想要的粒子效果:完整代码上传到github:https://github.com/QuarkGluonPlasma/threejs-exercizeSummary粒子动画是构成物体的基本单位的运动。对于3D来说,就是顶点的移动。我们要实现“星辰送福”的粒子动画,即从星星的顶点到福字顶点的运动。可以随机生成星星的顶点,使用BufferGeometry创建对应的几何体。“赋”字是加载创建好的3D模型,获取其中的顶点位置。有了起始位置和结束位置,就可以实现粒子动画了。过程中的x、y、z值是使用动画库Tween.js计算的,可以指定加减速等时间函数。粒子动画有一种打散重组的感觉,可以用来做一些很酷的效果。了解什么是粒子动画,什么在粒子动画中运动,即使你最初已经掌握了。我摘下天上的星星,给大家祝福。让我们为新的一年加油!