全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2023-12-03_尝试用 three.js 实现了这个跨窗口的粒子动画

您的位置:首页 >> 新闻 >> 行业资讯

尝试用 three.js 实现了这个跨窗口的粒子动画 点击小卡片参与粉丝专属福利 相信不久前大家已经被这个粒子动画给刷屏了吧: bcab9963-4461-4964-96a0-214a15626f75之前看过很多人都在分析跟这个效果相关的跨窗口通信技术,尽管这样是挺好,但我依旧有点纳闷:为什么没有人讲讲背后的粒子特效是怎么实现的呢? 于是乎,我花了一整个周末,把这个粒子特效给肝了出来。尽管可能跟原特效相比有一点差距,但也表明了这种效果是可以实现的。 entangled在线体验:https://entangled-fxhash.netlify.app/ 本文就让我们来看看实现这个粒子特效有哪些要点吧~ GPGPU当我第一眼看到原特效时,发现那个球体是由大量的粒子组成的,第一反应就是THREE.Points,但是又转念一想,光这个是不够的,因为在粒子量足够大的情况下,同时渲染大量的运动粒子肯定会遇到性能上的瓶颈。 然后,我又想到了另一个技术——GPGPU,中文翻译是“图形处理器通用计算”,尽管这个概念看上去很复杂,但其实也很简单——利用GPU的并行特性来执行一些和图形渲染无关的计算任务,如模拟粒子系统等。在粒子非常多的情况下,用它就能满足性能上的需求。 three.js的官方文档上有一个模拟鸟群飞行的效果,就用到了这个技术,可以把鸟群飞行看作是一个不断运动着的粒子系统。 4307749b-15a0-47a4-b562-e34f27212e68那么如何使用GPGPU呢?大致步骤如下: 创建GPGPU对象,three.js里是GPUComputationRenderer;创建数据纹理,用来存储需要GPU计算的数据;创建GPGPU变量,每个变量需要一个对应的Compute Shader(计算着色器),用来处理变量的计算逻辑;初始化GPGPU,并在渲染循环中使其不断计算,将GPGPU变量计算得来的结果作为纹理变量赋给需要的材质。以下是我的粒子效果用到的相关代码: constwidth=512; constsize=256; constcount=width**2;//我们总共要实时计算 512x512=262144 个粒子! //创建GPGPU对象(kokomi.GPUComputer是一个对GPUComputationRenderer的封装) constgpgpu=newkokomi.GPUComputer(this.base,{ width, }); //创建数据纹理 constposDt=gpgpu.createTexture(); constdata=posDt.image.data; //为数据纹理填充随机数据 for(leti=0;idata.length;i++){ data[i*4+0]=THREE.MathUtils.randFloatSpread(size); data[i*4+1]=THREE.MathUtils.randFloatSpread(size); data[i*4+2]=THREE.MathUtils.randFloatSpread(size); data[i*4+3]=1; } //创建GPGPU变量 constposVar=gpgpu.createVariable( "texturePosition", testObjectComputeShader, posDt, { uFreq:{ value:1, }, ... } ); //初始化GPGPU gpgpu.init(); 粒子的本体还是用THREE.Points,只不过geometry用的是自定义的BufferGeometry,里面可以填充一些随机的位置数据position,同时也要加上用来采样GPGPU纹理的UV坐标reference。 constgeometry=newTHREE.BufferGeometry(); constpositions=newFloat32Array(count*3); constreferences=newFloat32Array(count*2); for(leti=0;iwidth;i++){ for(letj=0;jwidth;j++){ constidx=i+j*width; positions[idx*3+0]=Math.random(); positions[idx*3+1]=Math.random(); positions[idx*3+2]=Math.random(); references[idx*2+0]=i/width; references[idx*2+1]=j/width; } } geometry.setAttribute("position",newTHREE.BufferAttribute(positions,3)); geometry.setAttribute("reference",newTHREE.BufferAttribute(references,2)); 材质的话就用自定义着色器材质ShaderMaterial。 constmaterial=newTHREE.ShaderMaterial({ vertexShader:testObjectVertexShader, fragmentShader:testObjectFragmentShader, uniforms:{ texturePosition:{ value:null, }, uPointSize:{ value:1, }, uPixelRatio:{ value:this.base.renderer.getPixelRatio(), }, }, transparent:true, blending:THREE.AdditiveBlending, depthWrite:false, }); 顶点着色器中,我们要采样GPGPU帮我们计算得来的粒子位置。 uniform float uPointSize; uniform float uPixelRatio; uniform sampler2D texturePosition; attribute vec2 reference; void main(){ vec3 p=texture(texturePosition,reference).xyz; gl_Position=projectionMatrix*modelViewMatrix*vec4(p,1.); gl_PointSize=uPointSize*uPixelRatio; } 片元着色器直接输出粒子的颜色即可。 void main(){ vec4 col=vec4(0.,1.,0.,1.); gl_FragColor=col; } 然后就是创建微粒对象,并将它放到场景中,在渲染循环中给它的材质注入GPGPU计算得来的纹理数据。 constpoints=newTHREE.Points(geometry,material); this.scene.add(points); this.update(()={ constmat=points.material; mat.uniforms.texturePosition.value=gpgpu.getVariableRt(posVar); }); 准备工作都已到位,接下来让我们开始进入真正的重头戏——Compute Shader。 Compute Shader计算着色器的格式跟片元着色器类似,都是输出gl_FragColor,只不过这里的color代表的是GPU变量的值,在这里,我们用texturePosition作为了GPU变量,也就是粒子的位置,在着色器顶部声明好它。 uniform sampler2D texturePosition; 在主函数中,我们计算好UV坐标,并用它来采样texturePosition纹理。 void main(){ vec2 uv=gl_FragCoord.xy/resolution.xy; vec3 pos=texture(texturePosition,uv).xyz; gl_FragColor=vec4(pos,1.); } dea09304-70bd-4a36-befa-ebdeb15baf4b这里我们直接输出了位置纹理的原始数据,可以看到就是一堆随机的离散点。 是时候让这些点动起来了!给位置变量pos应用噪声函数吧,我选择了卷曲噪声curl。 ?这里用到了Shader函数库lygia,里面有很多实用的噪声函数。 ?#include "/node_modules/lygia/generative/curl.glsl" void main(){ vec2 uv=gl_FragCoord.xy/resolution.xy; vec3 pos=texture(texturePosition,uv).xyz; pos=curl(pos); gl_FragColor=vec4(pos,1.); } a9082480-2048-4d84-a280-9dbdf34cf7d3之前离散的点立马就变成了一个美丽的噪声球体。 除了单个卷曲噪声外,我们还可以用fbm,产生一个更加混沌的结果。 vec3 fbm(vec3 p){ vec3 value=p; float amplitude=.5; float frequency=2.; float lacunarity=2.; float persistance=.5; float scale=.5; int octaves=1; for(int i=0;ioctaves;i++){ vec3 noiseVal=curl(value*frequency*scale); value+=amplitude*noiseVal; frequency*=lacunarity; amplitude*=persistance; } return value; } void main(){ vec2 uv=gl_FragCoord.xy/resolution.xy; vec3 pos=texture(texturePosition,uv).xyz; pos=curl(pos*uFreq); pos=fbm(pos); gl_FragColor=vec4(pos,1.); } b096ebdf-1f0f-4e78-819d-896dfedc5a10但这个结果太混沌了,跟之前的球体相差有点大。我们可以用mix函数来将它和球体有机地混合起来,混合因子也可以用其他的噪声函数来计算。 void main(){ vec2 uv=gl_FragCoord.xy/resolution.xy; vec3 col=vec3(0.); vec3 pos=texture(texturePosition,uv).xyz; pos=curl(pos*uFreq); col=pos; vec3 pos2=texture(texturePosition,uv).xyz; pos2=curl(pos2*uFreq); pos2=fbm(pos2); col=pos2; float mixFactor=0.; mixFactor=cnoise(pos+iTime)*.5; col=mix(pos,pos2,mixFactor); gl_FragColor=vec4(col,1.); } fd7f556a-3652-d64f-3b53-a4d3708e9b58_(1)此外,我们可以做一个拉伸球的动画,用作后面纠缠动画的基础,思路如下:定义一个牵引点,用sdf函数算出牵引距离,再求得牵引方向向量,给位置变量应用它们的乘积即可。 #include "/node_modules/lygia/sdf/boxSDF.glsl" void main(){ vec2 uv=gl_FragCoord.xy/resolution.xy; ... vec3 attract=uAttract; float attractX=abs(attract.x); float d=boxSDF(col-attract,vec3(attractX,.25,.25)); vec3 dir=normalize(col-attract); col-=dir*smoothstep(.2,.0,d)*(attractX-1.); gl_FragColor=vec4(col,1.); } Shader项目模板_(1)至于两球相互纠缠,emmmm,说实话我直接用了原作者开源的项目里的WindowManager来实现的,把里面的方块替换成了我的粒子球体,最终结果就是文章开头的那个动画。 最后粒子效果本身差不多就这样完成了。还有一些待优化的点: 不用卷曲噪声“逃课”,用更加精美的扭曲方式来美化球体。纠缠动画有一点生硬,并不像原效果那样丝滑、连贯。主要还是第 2 点吧,感觉原作者是用了一种特定的算法来实现的,只可惜我不会 QAQ,还得继续努力呢~ 源码Github:https://github.com/alphardex/entangled 如果文章对你有帮助的话欢迎「关注+点赞+收藏」 阅读原文

上一篇:2020-06-15_EA出品2020版「灌篮高手」:运球、抢断生成敏捷流畅,帮你找回童年梦想 下一篇:2024-10-06_AI博士如何做出有影响力的研究?斯隆奖得主弟子亲身讲述经验

TAG标签:

19
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价