当前位置: 首页 > Web前端 > vue.js

Threejs+vue3实现烟花效果

时间:2023-03-31 23:42:27 vue.js

前言今年因为疫情和过年没回家,抽空看了WebGL编程指南和Three.js开发指南。为了练习,简单实现了冬奥会开幕式欢迎嘉宾松烟花效果,纯白色,本文主要记录练习过程,同时也涉及到threejs最基础的内容,下面开始吧...效果图如下,也可以点击链接预览https://awebgl.vercel.app/首先是一个开门的动作,然后机器人是threejs官网的例子,还有最后是烟花飞上天,炸成迎客松的效果。环境使用vite+vue3,步骤如下,threejs的api比较长,暂时记不住了(捂脸),npm安装了@types/three,vscode有代码提示功能,还有还安装了一个动画库tween.js。npmcreatevite@latestthree3d--templatevuecdthree3dnpminstall@tweenjs/tween.js@types/threethree-S实现个人感觉做3d,完全可以把自己当成导演(笑),摄像头放哪里,打光哪里等,不然很容易遇到全屏黑屏,下面是我的例子的草图,大家可以根据自己的习惯搭建。场景和相机首先,您需要创建一个场景,然后您可以在场景中添加光照和图元。那么本例使用透视相机PerspectiveCamera(fov,aspect,near,far),可以提供近、大、远的3D视觉效果。纵横比通常设置为画布的纵横比。这里我选择整个页面。所以就是window.innerWidth/window.innerHeight,最后把相机对准中心(0,0,0)。//App.vueconstcreateScene=()=>{scene=newTHREE.Scene();//创建场景}constcreateCamera=()=>{constscale=window.innerWidth/window.innerHeight;相机=新三。PerspectiveCamera(60,scale,0.1,1000);camera.position.set(0,0,20)camera.lookAt(newTHREE.Vector3(0,0,0))}Lighting首先加入环境光,AmbientLight只是简单的将材质的颜色和光的颜色叠加,然后乘以光强度。您可以参考下面代码的注释部分。由于是晚上,我选择了深一点的颜色。也因为是晚上,所以没有像太阳光那样使用平行光,而是在大门左右各增加了一个类似路灯效果的射灯光源。//App.vueconstcreateLight=()=>{//环境光//这里的颜色计算是分别乘以RBG通道上的值//例子:rgb(0.64,0.64,0.64)=rgb(0.8,0.8,0.8)*rgb(0.8,0.8,0.8)*1//颜色=materialColor*light.color*light.intensity;constambientLight=newTHREE.AmbientLight(0x1c1c1c);环境光强度=1;scene.add(环境光);//聚光灯constspotLight1=newTHREE.SpotLight(0xffffff,1);spotLight1.position.set(-1.5,1.5,10);spotLight1.target.position.set(-1.5,-2,8);spotLight1.castShadow=true;spotLight1.shadow.mapSize.width=2048;spotLight1.shadow.mapSize.height=2048;spotLight1.shadow.camera.near=1;spotLight1.shadow.camera.far=100;spotLight1.shadow.camera.fov=30;spotLight1.penumbra=1;scene.add(spotLight1);scene.add(spotLight1.target);//同样的方法添加合适的灯光...}原始模型相机灯光舞台搭建完成,接下来就是我们的主角登场了。小分支:见下图示意图,选中圆的一部分,移动到原点,然后绕z轴旋转圆弧,左右生成4个分支。旋转后点的计算公式可以直接套用到数学公式中。这里需要说明一点,threejs使用的是右手坐标系,z轴垂直于屏幕指向外,所以原理图绕z轴逆时针旋转θ',得到右边的四个弧也是负数。最后把这个树枝的scale设置为0,再放大到1,就可以简单的实现一个爆炸的效果。迎客松全枝:先克隆上面60个小枝,然后在16×4×2立方体的区域内随机选择60个位置设置小枝,最后分3批依次开花。迎宾松树干:斜5行,竖10行。效果和上面类似。大家可以直接看源码,我就不再介绍了。//App.vueconstpointsMaterial=newTHREE.PointsMaterial({size:0.15,sizeAttenuation:true,transparent:true,opacity:0.8,color:0xffffff,depthWrite:false,blending:THREE.AdditiveBlending,vertexColors:true});const圈数=8;//8条弧线constcirclePointNum=circlePoint*3*circleNum;constcircleColors=getColors(circlePointNum);让circleArr=[];//右4条弯曲线左4条弯曲线for(leti=0;i<4;i++){circleArr=circleArr.concat(getRightPosition(-1*i/12)).concat(getLeftPosition(i/12)));}constcircleGeometry=newTHREE.BufferGeometry();circleGeometry.setAttribute("color",newTHREE.BufferAttribute(circleColors,3))circleGeometry.setAttribute("position",newTHREE.BufferAttribute(newFloat32Array(circleArr),3))circleGeometry.attributes.position.needsUpdate=true;constcirclepoints=newTHREE.Points(circleGeometry,pointsMaterial);constcirclegroup=newTHREE.Group()circlegroup.add(圆点);circlegroup.visible=falsecirclegroup.scale.set(0,0,0)constflowerGroup=newTHREE.Group();//60个随机小烟花for(leti=0;i<60;i++){lettgroup=circlegroup.clone();tgroup.position.set(16*Math.random()-8,4*Math.random()+5,-2*Math.random())flowerGroup.add(tgroup);}flowerGroup.position.set(0,0,-2)scene.add(flowerGroup);Robot:这个是threejs官网的例子,链接https://threejs.org/examples/...,添加这个是因为开门放烟花感觉有点单调,所以用了这个robot。我用了挥手、跑、跳三个动作。需要说明的是,在加载机器人模型时,如果不等待加载完成,则继续下一步。一步操作,会黑屏,感觉跟vue框架有关系,最后用Promise打包。//App.vueletpeople=awaitloadMesh(peopleModel);people.scene.traverse((child)=>{if(child.isMesh){child.castShadow=true;}})people.scene.position.set(0.5,-5,4);people.scene.scale.set(0.6,0.6,0.6);//加载动画混合器=newTHREE.AnimationMixer(people.scene);让动画=people.animations;让jumpClip=mixer.clipAction(animations[3])让runClip=mixer.clipAction(animations[6])让waveClip=mixer.clipAction(animations[12])waveClip.play()scene.add(people.scene)门:这个block需要提到的是图元旋转的时候都是围绕中心点。我使用组合组,左门向右偏移门宽度的一半。同样将右边的门向左偏移,然后将Group分别向左和向右移动门的整个宽度,最后围绕y轴旋转Group,达到开门的效果。门的正面贴有纹路,其他面都是红色的。官网上找不到立方体表面对应的贴图。顺序,我试了一下应该是[右、左、上、下、前、后]。//App.vueconstdoorWidth=3;常量doorHeight=6;constcubeGeometry=newTHREE.BoxGeometry(doorWidth,doorHeight,0.5);constleftdoorTexture=awaitloadTexture(leftDoorPic);constrightdoorTexture=awaitloadTexture(rightDoorPic);constcubeMater=newTHREE.MeshPhongMaterial({color:0x5C0400});constcubeMaterial2=newTHREE.MeshPhongMaterial({map:leftdoorTexture});constcubeMaterial3=newTHREE.MeshPhongMaterial({map:rightdoorTexture});constdoorGroup1=newTHREE.Group()constdoorCube1=newTHREE.Mesh(cubeGeometry,[cubeMaterial1,cubeMaterial1,cubeMaterial1,cubeMaterial1,cubeMaterial2,cubeMaterial1]);doorCube1.castShadow=true;doorCube1.position.x=doorWidth/2;doorGroup1.position.set(-doorWidth,doorHeight/2-5,8);doorGroup1.add(doorCube1)scene.add(doorGroup1);//同样的方法在右侧添加门...最后简单梳理一下整个动画过程,大概是门开了-》机器人挥手-》跑到鞭炮旁-》鞭炮起飞-》烟花绽放,动画用的是tweenjs库,api也很简单,就不多说了。渲染首先创建WebGLRenderer渲染器,设置设备像素比,启用阴影等,最后在页面中添加domElement,也就是画布。然后创建一个update方法,调用requestAnimationFrame更新动画,为了方便查看,创建一个OrbitControls轨道控制器,可以让相机围绕目标(0,0,0)进行轨道运动。//App.vueconstcreateRender=()=>{renderer=newTHREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth,window.innerHeight);渲染器.shadowMap.enabled=true;container.value.appendChild(renderer.domElement);}constupdateRender=()=>{requestAnimationFrame(updateRender)renderer.render(scene,camera);orbitControls&&orbitControls.update();TWEEN.update();lettime=clock.getDelta()mixer&&mixer.update(time)}onMounted上面的方法都创建好了,最后在onMounted中调用就可以显示一个3D场景了,基本就是下面几步。//App.vueonMounted(()=>{createScene();//创建场景createCamera();//创建相机createLight();//创建灯光createMesh();//加载模型createRender();//创建RenderingcreateControl();updateRender();//更新渲染window.addEventListener('resize',onWindowResize,false);})最后附上源码地址:https://github.com/chencld/th...好久没写文章了。拖了半个月,终于写完了。暗语并不容易。请给我多多点赞。也欢迎在讨论区交流,谢谢~