大家好,我是大傻。近些日子,想必到大家也看了新闻,我们的热气球由于风向问题,不小心飞到了pl国,结果引起了pl国的过度防范,在此,大傻还原了当时的场景,一起来看看吧。

开始 前期准备工作

先来看下效果吧。

素材思考

根据效果,我们首先先来捋一遍我们需要的素材。

  • 热气球(可以拿上一篇的孔明灯来代替)

  • 大炮模型(溜溜网有免费素材)

  • 房屋模型(溜溜网)

内容思考

根据效果,我们可以总结出以下几点

  • 场景需要一个光源,我们没有去引入HDR作为环境

  • 大炮发射可以分为三个步骤

    • 发射线性炮弹

    • 轨迹消失 炮弹炸裂开来

    • 炮弹的炸裂呈圆形效果且颜色随机

  • 热气球的位置有一定的约束(相对于炮弹的位置约束)

技术思考

在上一篇中我们是用了gsap最终完成了热气球的动画模拟,这一篇中我们可以通过着色器shader使用来完成炮弹的轨迹动画,我们在此拆分下需要完成的工作。

  • 创建热气球函数(也可以和上篇一样,循环生成)

  • 创建炮弹轨迹函数

  • 创建最终爆炸效果函数

  • 通过点击调用我们的创建函数,并在相对时间后调用另外一个函数

中期 开发阶段

项目初始化

话不多说,首先还是我们的素材以及package.json,素材链接[1]

{"name": "biubiubiu","version": "1.0.0","description": "","main": "main.js","scripts": {"dev": "parcel src/index.html","build": "parcel build src/index.html"},"author": "","license": "ISC","devDependencies": {"@parcel/transformer-glsl": "2.8.3","parcel": "^2.4.1"},"dependencies": {"dat.gui": "^0.7.9","gsap": "^3.11.4","three": "^0.139.2"}
}复制代码

炮弹函数封装

热气球函数我们就不多解释了,感兴趣的可以按照大傻上一篇的内容进行创建,我们先来对炮弹进行封装, 首先我们需要对调用进行分析,每次我们点击时候都会创建一个炮弹并且会爆炸开,那么我们可以封装一个类,目前我们已知条件中,我们需要传入颜色以及位置,也就是我们炮弹的爆炸位置以及发射时候的颜色。我们还需要一个addScene的函数用来给我们的文件引入当前环境的scene以及相机camera

let createShell = () => {
// 随机颜色let color = `hsl(${Math.floor(Math.random()*360)},100%,80%)`let position = {// x: 0,x: 5,z: 10,y: 40}// 颜色随机生成 位置let shell = new Shell(color, position)shell.addScene(scene, camera)scene.push(shell)
}
复制代码

根据以上的信息我们先来模拟一下炮弹函数的结构

import * as THREE from 'three'
export default class Shell {constructor(color, to, from = {x:15,y:15,z: -15}) {// 这里我们对炮弹以及炮弹炸裂效果进行处理}// 添加到场景addScene(scene, camera) {// 这里我们对生成的炮弹以及效果 添加到场景}// 更新变量update() {// 在这对时间进行把控 也就是我们炮弹或者效果 执行完后进行回收操作}}
复制代码

接下来我们先对炮弹进行处理,这里我们模拟的炮弹为点结构

this.color = new THREE.Color(color)// 初始化炮弹物体this.startGeometry = new THREE.BufferGeometry()// 点成线 需要的多个点const startPointCount = 50 + Math.floor(Math.random() * 50);// 初始位置const startPositionArray = new Float32Array(3*startPointCount)// 初始的大小const startPointScaleFireworkArray = new Float32Array(startPointCount)// 运动偏移位置const startPointDirectionArray = new Float32Array(startPointCount * 3)for (let i = 0; i < startPointCount; i++) {// 一开始炮弹的位置startPositionArray[i * 3 + 0] = to.x - from.x;startPositionArray[i * 3 + 1] = to.y - from.y;startPositionArray[i * 3 + 2] = to.z - from.z;// 设置炮弹初始大小startPointScaleFireworkArray[i + 0] = Math.random();// 设置炮弹大小 轨迹是一条线 所以我们没有角度参与let r = Math.random();startPointDirectionArray[i * 3 + 0] = r *(  from.x)*0.1startPointDirectionArray[i * 3 + 1] = r*( from.y)*0.1startPointDirectionArray[i * 3 + 2] = r *( from.z)*0.1}// 设置位置this.startGeometry.setAttribute('position', new THREE.BufferAttribute(startPositionArray, 3))//设置轨迹this.startGeometry.setAttribute('aRandom', new THREE.BufferAttribute(startPointDirectionArray, 3))// 设置着色器材质this.startMaterial = new THREE.ShaderMaterial({vertexShader,fragmentShader,transparent: true,blending: THREE.AdditiveBlending,depthWrite: false,uniforms: {uTime: {value: 0},uSize: {value: 2},uColor: {value: this.color}}})// 创建炮弹点球this.startPoint = new THREE.Points(this.startGeometry, this.startMaterial);复制代码

紧接着是我们的炮弹着色器,首先是顶点着色器

attribute vec3 aRandom;
uniform float uTime;
uniform float uSize;
void main(){// 模型位置 vec4 modelPosition=modelMatrix*vec4(position,1.);// 将我们的模型位置加上我们的轨迹乘以时间 得到我们最终的方向位置modelPosition.xyz+=(aRandom*uTime)*5.;// 最终渲染位置就是我们的 视图矩阵以及模型位置的乘积gl_Position=projectionMatrix*viewMatrix*modelPosition;// 点的着色器必须有一个初始的大小gl_PointSize=uSize;
}
复制代码

片元着色器

uniform vec3 uColor;
void main(){// 生成点状结构float strength=distance(gl_PointCoord,vec2(.5));strength*=2.;strength=1.-strength;// strength=step(.5,strength);// 渲染颜色gl_FragColor = vec4(uColor,strength);
}
复制代码

image.png

炮弹爆炸效果函数

首先,我们先思考下,炮弹爆炸效果其实类似于我们创建炮弹的过程,只不过我们创建炮弹时候,是让炮弹做线性运动的,而爆炸就是向四周进行运动,由此我们来写下代码

// 开始计时this.clock = new THREE.Clock()// 创建爆炸this.blastGeometry = new THREE.BufferGeometry()this.blastCount = 180 + Math.floor(Math.random() * 180);const positionBlastArray = new Float32Array(this.blastCount * 3)const scaleBlastArray = new Float32Array(this.blastCount)const directionArray = new Float32Array(this.blastCount * 3)for (let i = 0; i < this.fireworkCount; i++) {// 一开始的位置positionBlastArray[i * 3 + 0] = to.x;positionBlastArray[i * 3 + 1] = to.y;positionBlastArray[i * 3 + 2] = to.z;// 设置初始大小scaleBlastArray[i + 0] = Math.random();// 设置旋转角度 这里是为了爆炸的点可以四散开来let theta = Math.random() * 2 * Math.PI;let beta = Math.random() * 2 * Math.PI;let r = 1;directionArray[i * 3 + 0] = r * Math.sin(theta) + r * Math.sin(beta);directionArray[i * 3 + 1] = r * Math.cos(theta) + r * Math.cos(beta);directionArray[i * 3 + 2] = r * Math.sin(theta) + r * Math.cos(beta);}this.blastGeometry.setAttribute('position', new THREE.BufferAttribute(positionFireworksArray, 3))this.blastGeometry.setAttribute('aScale', new THREE.BufferAttribute(scaleFireworkArray, 1))this.blastGeometry.setAttribute('aRandom', new THREE.BufferAttribute(directionArray, 3))this.blastMaterial = new THREE.ShaderMaterial({vertexShader: blastVertexShader,fragmentShader: blastFragmentShader,transparent: true,blending: THREE.AdditiveBlending,depthWrite: false,uniforms: {uTime: {value: 0},uSize: {value: 0},uColor: {value: this.color}}})this.blastPoint = new THREE.Points(this.blastGeometry, this.blastMaterial);
复制代码

那么接下来就是我们爆炸效果的顶点着色器了

attribute float aScale;
attribute vec3 aRandom;
uniform float uTime;
uniform float uSize;
// 这块逻辑和我们的炮弹逻辑一样 唯一不同点在于大小的设置
void main(){vec4 modelPosition=modelMatrix*vec4(position,1.);modelPosition.xyz+=(aRandom*uTime)*5.;gl_Position=projectionMatrix*viewMatrix*modelPosition;// 因为我们爆炸后 点的大小 由大到小 所以在此和时间作一个运算gl_PointSize=uSize*aScale-(uTime*10.);
}
复制代码

片元着色器

uniform vec3 uColor;void main(){float strength=distance(gl_PointCoord,vec2(.5));strength*=2.;strength=1.-strength;strength=step(.5,strength);gl_FragColor=vec4(uColor,strength);}
复制代码

更新函数

截至目前我们已经创建了我们的炮弹以及我们的爆炸函数,是时候该让他们整合起来发挥效果了,首先就是先去添加到我们的场景中去

addScene(scene, camera) {scene.add(this.startPoint)scene.add(this.blastPoint)this.scene = scene;}
复制代码

接下来就是我们的更新函数,作用主要在于在合适的时机去触发我们的生成以及爆炸以及最后的消失

const elapseTime = this.clock.getElapsedTime();if (elapseTime > 0.2 && elapseTime < 1) {// 当时间在0.2-1时候 让我们的炮弹以线性运动显示this.startMaterial.uniforms.uTime.value = elapseTime;this.startMaterial.uniforms.uSize.value =1} else if (elapseTime >= 1) {// 大于1后就让我们炮弹消失  爆炸效果显示const time = elapseTime - 1// 让元素消失this.startMaterial.uniforms.uSize.value = 0;this.startPoint.clear()this.startGeometry.dispose()this.scene.remove(this.startPoint)// 设置显示this.blastMaterial.uniforms.uSize.value = 20this.blastMaterial.uniforms.uTime.value = timeif (time > 5) {// 超过5后 就给我们场景进行一次清空处理this.blastPoint.clear()this.blastGeometry.dispose()this.blastMaterial.dispose()this.scene.remove(this.firePoint)this.scene.remove(this.startPoint)}}
复制代码

结尾讨论

至此为止,我们的流浪气球就已经完成了 看下最终结果吧

有什么问题,欢迎大家指教,PS:这个射线轨迹一直没在炮口上,位置太难调了

关于本文

作者:王大傻

https://juejin.cn/post/7197608023429808183

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

使用 ThreeJS 还原「流浪气球」相关推荐

  1. 张洁的新书「流浪的老狗」

    张洁的最新书「流浪的老狗」终于出版.这是她第一部文学和摄影的游记,纪录了她一个人背着行囊游历世界的感悟.她驻足的地方大多是不为外人所知的小角落,她深入到世界最有特色和活力的肌理当中,记下她的独特感受和 ...

  2. 拯救流浪猫 | 「喵先锋」系列数字版权盲盒明日开抢

    无界版图X奇帧元界  「喵先锋」系列 WORLD OF  CAT 「喵先锋」系列   2022年8月2日 11:00-14:00优先购 发售通知 ▷活动预告 平台将于北京时间2022年8月2日以盲盒形 ...

  3. 网络热传App鉴定 |「得物」疑私删用户视频?从技术角度还原事件始末

    声明:本文更注重于原理知识的普及,因此文中不会有大量实际代码的展示,如果想从代码层面上了解「应用存储分区」的内容,欢迎阅读我两年前写过的技术文章<Android 10 应用分区存储适配实践> ...

  4. 屏幕使用时间忘了能还原吗_忘了 iOS 设备上的「屏幕使用时间」密码怎么办?| 一日一技...

    苹果在 iOS 12 系统中推出了一个「屏幕使用时间」的功能,可以帮助我们限制自己的使用手机时间.除了提供详尽的数据统计,「屏幕使用时间」功能还可以通过设置密码来强制关闭 app 的使用权限,达到减少 ...

  5. 看过漫改,但你看过「改漫」吗?AI 一键让影视变漫画

    作者 | 神经小兮 来源 | HyperAI超神经 头图 | 下载于视觉中国 把影视剧变成漫画,是怎样的一种神操作?来自大连理工大学和香港城市大学的团队,最新提出的 AI 框架,可自动将影视剧转换为漫 ...

  6. 一副耳机的钱,竟让我体验了「元宇宙」办公

      视学算法报道   编辑:桃子 [新智元导读]一个防疫政策,又是居家办公.视频会议2分就卡顿,电话会议各说各的,都元宇宙时代了,打工人远程协同工作就这么难? 都元宇宙时代了,为啥打工还这么苦bi? ...

  7. 5分钟带你读「大清」微积分!160多年前清朝数学家撰写文言文版高等数学

     视学算法报道   编辑:小咸鱼 好困 [新智元导读]你有见过160多年前清朝数学家写的微积分书吗?这可能是最难懂的高数教材了,堪称天书!近日,网上流传着一本清朝的微积分课本,其中的所有数学表达式都是 ...

  8. 竟有内鬼!北理工硕士生「复制粘贴」论文,旷视研究员最新声明

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|AI算法与图像处理 [导读]过了半个多月,北理工硕士公然抄 ...

  9. 东汉末年,他们把「服务雪崩」玩到了极致(干货)

    来源 | 悟空聊架构(ID:PassJava666) 滚滚长江东逝水,浪花淘尽英雄. 是非成败转头空.青山依旧在,几度夕阳红. -- 来自<三国演义> 本篇将会通过三国中的赤壁之战来讲述周 ...

最新文章

  1. 深度学习---TensorFlow学习笔记:搭建CNN模型
  2. leetcode 138. Copy List with Random Pointer | 138. 复制带随机指针的链表(复杂链表的复制)
  3. 项目分享 | 好牛X的开源项目,看完忍不住分享(高手作品分享)
  4. python3.x的改进
  5. C#:Md5和Sha1两种加密方式
  6. 2017华为软件精英挑战赛思路分析
  7. 黑金AX7Z100 FPGA开发板移植LWIP库(一)PS端
  8. 深度清理电脑垃圾软件 如何深度清理电脑垃圾
  9. 服务器如何挂网页游戏,网页游戏怎么挂云服务器
  10. 在linux下恶臭hellotext中作用的?,《Linux内核与程序设计》实验学习笔记
  11. tensorflow正则化添加方法整理
  12. centos7.4配置nginx php,centos7.4下安装配置PHP服务(源码安装)并配置nginx支持php
  13. 计算机丢失explorer,xp系统如何找回电脑桌面上消失不见的 internet explorer图标
  14. Oracle Tigger触发器 实例
  15. 开源好物分享!文档在线预览平台
  16. 商业智能bi时代:商务智能常见应用实例
  17. ICCBDAI 2022 - 十二月无数计算机学者齐聚张家界
  18. SCYC55830 58063282A可控硅触发电路
  19. 让你的IDEA如丝滑般顺畅
  20. 使用sale数据库,统计全部商品的平均价格,以及文具类商品的平均价格

热门文章

  1. Oracle性能调整的三把利剑--ASH,AWR,ADDM
  2. Java语言发展简史
  3. 中国历史上十大经典遗言
  4. 前端适配不同型号手机分辨率,100%还原UI设计稿的方案实践
  5. Redis数据库(入门)
  6. 在线客服——各第三方的收费标准及服务提供
  7. 学习AngularJS摘抄的笔记,方便以后查看(摘自菜鸟教程)
  8. 分享5款堪称神器的免费软件,建议先收藏再下载
  9. 谷歌Chrome浏览器
  10. Fiddler修改返回数据教程,亲测有效