Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产一区二区三区在线看片,欧美一级淫片免费视频日本动漫,视频一区二区在线

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Unity教程:怎么快速制作星空粒子效果?

          Unity教程:怎么快速制作星空粒子效果?

          們都知道,Unity中自帶了一些粒子效果,在Assets>ImportPackage>Particles,即可將Prticles.UnityPackage導(dǎo)入到項(xiàng)目中,這些粒子效果包括:Dust(沙塵)、Fire(火焰)、Water(水)、Smoke(煙霧)、Sparkles(閃光),還有一些粒子資源 Sources、Misc(雜項(xiàng))等。

          粒子特效能夠?yàn)橛螒蛟鎏斫换ヅc響應(yīng)能力,它們擅長(zhǎng)創(chuàng)造許多運(yùn)動(dòng)和撞擊效果。粒子特效可用于創(chuàng)建魔法火球,漩渦狀的空間傳送門,或者將玩家的注意力引導(dǎo)到一個(gè)發(fā)光的寶箱。炫酷的視覺效果往往引人入勝。

          今天,我們帶著大家通過快速制作星空特效,來認(rèn)識(shí)一下Unity的粒子效果。

          1、首先,新建一個(gè)場(chǎng)景,如果有自己的天空盒資源的話,在Window->Lighting下設(shè)置下天空(默認(rèn)天空盒也不影響演示)。

          2、新建一個(gè)空對(duì)象命名為Star,為其添加Particle System組件。注意:一個(gè)對(duì)象最多只能有一個(gè)Particle System組件。

          3、勾選Prewarm。字面意思就是預(yù)熱。就是場(chǎng)景一開始,就已經(jīng)有很多粒子(粒子產(chǎn)生和消失已經(jīng)平衡),如果不勾選,一開始什么都沒有,等一會(huì)粒子數(shù)才變多。

          4、設(shè)置Start Lifetime(粒子的壽命(開始時(shí)))。由于星星一般移動(dòng)比較慢,例子壽命(秒數(shù))設(shè)置的長(zhǎng)一點(diǎn)。

          5、Emission模塊保持勾選,無需改動(dòng)保持默認(rèn)即可。如果希望加快星星的產(chǎn)生,可以增大Rate over Time選項(xiàng)。

          6、在Shape下,我們修改的是粒子生成裝置的形狀。我們改成一個(gè)Box(我們希望星星是從一個(gè)大盒子里生成的)

          7、設(shè)置盒子的大小 BoxX/Y/Z設(shè)置為100。同時(shí)Emit from設(shè)置為Volume, 意思是從整個(gè)體積均勻生成。(也可以設(shè)置成從盒子底部生成)

          8、展開Renderer,為Material屬性賦值,設(shè)置粒子的樣子(材質(zhì))。使用自帶的Default-Particle就可以。

          最終效果如圖。

          今天的教程,就為大家介紹到這里,希望大家可以學(xué)以致用,在游戲中創(chuàng)作出精彩的粒子特效!

          子動(dòng)畫“ 這個(gè)詞大家可能經(jīng)常聽到,那什么是粒子動(dòng)畫呢?


          粒子是指原子、分子等組成物體的最小單位。在 2D 中,這種最小單位是像素,在 3D 中,最小單位是頂點(diǎn)。


          粒子動(dòng)畫不是指物體本身的動(dòng)畫,而是指這些基本單位的動(dòng)畫。因?yàn)槭墙M成物體的單位的動(dòng)畫,所以會(huì)有打碎重組的效果。


          本文我們就來學(xué)習(xí)下 3D 的粒子動(dòng)畫,做一個(gè)群星送福的效果:


          思路分析

          3D 世界中,物體是由頂點(diǎn)構(gòu)成,3 個(gè)頂點(diǎn)構(gòu)成一個(gè)三角形,然后給三角形貼上不同的紋理,這樣就是一個(gè)三維模型。


          圖片

          也就是說,3D 模型是由頂點(diǎn)確定的幾何體(Geometry),貼上不同的紋理(Material)所構(gòu)成的物體(Mesh 等)。


          之后,把 3D 物體添加到場(chǎng)景(Scene)中,設(shè)置一個(gè)相機(jī)(Camera)角度去觀察,然后用渲染器(Renderer)一幀幀渲染出來,這就是 3D 渲染流程。


          3D 物體是由頂點(diǎn)構(gòu)成,那讓這些頂點(diǎn)動(dòng)起來就是粒子動(dòng)畫了,因?yàn)榛玖W觿?dòng)了,自然就會(huì)有打碎重組的效果。


          在“群星送福”效果中,我們由群星打碎重組成了福字,實(shí)際上就是群星的頂點(diǎn)運(yùn)動(dòng)到了福字的頂點(diǎn),由一個(gè) 3D 物體變成了另一個(gè) 3D 物體。


          那么群星的頂點(diǎn)從哪里來的?福字的頂點(diǎn)又怎么來呢?


          群星的頂點(diǎn)其實(shí)是隨機(jī)生成的不同位置的點(diǎn),在這些點(diǎn)上貼上星星的貼圖,就是群星效果。


          福字的頂點(diǎn)是加載的一個(gè) 3D 模型,解析出它的頂點(diǎn)數(shù)據(jù)拿到的。


          有了兩個(gè) 3D 物體的頂點(diǎn)數(shù)據(jù),也就是有了動(dòng)畫的開始結(jié)束坐標(biāo),那么不斷的修改每個(gè)頂點(diǎn)的 x、y、z 屬性就可以實(shí)現(xiàn)粒子動(dòng)畫。


          這里的 x、y、z 屬性值的變化不要自己算,用一些動(dòng)畫庫來算,它們支持加速、減速等時(shí)間函數(shù)。Three.js 的動(dòng)畫庫是 Tween.js。


          總之,3D 粒子動(dòng)畫就是頂點(diǎn)的 x、y、z 屬性的變化,會(huì)用動(dòng)畫庫來計(jì)算中間的屬性值。由一個(gè)物體的頂點(diǎn)位置、運(yùn)動(dòng)到另一個(gè)物體的頂點(diǎn)位置,會(huì)有種打碎重組的效果,這也是粒子動(dòng)畫的魅力。


          思路理清了,那我們來具體寫下代碼吧。


          代碼實(shí)現(xiàn)

          如前面所說,3D 的渲染需要一個(gè)場(chǎng)景(Scene)來管理所有的 3D 物體,需要一個(gè)相機(jī)(Camera)在不同角度觀察,還需要渲染器(Renderer)一幀幀渲染出來。


          這部分是基礎(chǔ)代碼,先把這部分寫好:


          創(chuàng)建場(chǎng)景:


          const scene=new THREE.Scene();

          創(chuàng)建相機(jī):


          const width=window.innerWidth;

          const height=window.innerHeight;

          const camera=new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

          相機(jī)分為透視相機(jī)和平行相機(jī),我們這里用的透視相機(jī),也就是近大遠(yuǎn)小的透視效果。要指定可以看到的視野角度(45)、寬高比(width/height)、遠(yuǎn)近范圍(0.1 到 1000)這 3 種參數(shù)。


          調(diào)整下相機(jī)的位置和觀察方向:


          camera.position.set(100, 0, 400);

          camera.lookAt(scene.position);

          然后是渲染器:


          const renderer=new THREE.WebGLRenderer();

          renderer.setSize(width, height);

          document.body.appendChild(renderer.domElement);

          渲染器要通過 requestAnimationFrame 來一幀幀的渲染:


          function render() {

          renderer.render(scene, camera);

          requestAnimationFrame(render);

          }

          render();

          準(zhǔn)備工作完成,接下來就是繪制星空、福字這兩種 3D 物體,還有實(shí)現(xiàn)粒子動(dòng)畫了。


          繪制星空

          星空不是正方體、圓柱體這種規(guī)則的幾何體,而是由一些隨機(jī)的頂點(diǎn)構(gòu)成的,這種任意的幾何體使用緩沖幾何體 BufferGeometry 創(chuàng)建。


          為啥這種由任意頂點(diǎn)構(gòu)成的幾何體叫緩沖幾何體呢?


          因?yàn)轫旤c(diǎn)在被 GPU 渲染之前是放在緩沖區(qū) buffer 中的,所以這種指定一堆頂點(diǎn)的幾何體就被叫做 BufferGeometry。


          我們創(chuàng)建 30000 個(gè)隨機(jī)頂點(diǎn):


          const vertices=[];

          for ( let i=0; i < 30000; i ++ ) {

          const x=THREE.MathUtils.randFloatSpread( 2000 );

          const y=THREE.MathUtils.randFloatSpread( 2000 );

          const z=THREE.MathUtils.randFloatSpread( 2000 );

          vertices.push( x, y, z );

          }

          這里用了 Three.js 提供的工具 MathUtils 來生成 0 到 2000 的隨機(jī)值。


          然后用這些頂點(diǎn)創(chuàng)建 BufferGeometry:


          const geometry=new THREE.BufferGeometry();

          geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(vertices, 3));

          給 BufferGeometry 對(duì)象設(shè)置頂點(diǎn)位置,指定 3 個(gè)數(shù)值(x、y、z)為一個(gè)坐標(biāo)。


          然后創(chuàng)建這些頂點(diǎn)上的材質(zhì)(Material),也就是星星的貼圖:


          圖片

          const star=new THREE.TextureLoader().load('img/star.png');

          const material=new THREE.PointsMaterial( { size: 10, map: star });

          頂點(diǎn)有了,材質(zhì)有了,就可以創(chuàng)建 3D 物體了(這里的 3D 物體是 Points)。


          const points=new THREE.Points( geometry, material );

          scene.add(points);

          看下渲染的效果:


          圖片

          靜態(tài)的沒 3D 的感覺,我們讓每一幀轉(zhuǎn)一下,改下 render 邏輯:


          function render() {

          renderer.render(scene, camera);

          scene.rotation.y +=0.001;


          requestAnimationFrame(render);

          }

          再來看一下:


          3D 星空的感覺有了!


          接下來我們來做粒子動(dòng)畫:


          3D 粒子動(dòng)畫

          3D 粒子動(dòng)畫就是頂點(diǎn)的動(dòng)畫,也就是 x、y、z 的變化。


          我們先來實(shí)現(xiàn)個(gè)最簡(jiǎn)單的效果,讓群星都運(yùn)動(dòng)到 0,0,0 的位置:


          起始點(diǎn)坐標(biāo)就是群星的的本來的位置,通過 getAttribute('position') 來取。動(dòng)畫過程使用 tween.js 來計(jì)算:


          const startPositions=geometry.getAttribute('position');


          for(let i=0; i< startPositions.count; i++) {

          const tween=new TWEEN.Tween(positions);


          tween.to({

          [i * 3]: 0,

          [i * 3 + 1]: 0,

          [i * 3 + 2]: 0

          }, 3000 * Math.random());


          tween.easing(TWEEN.Easing.Exponential.In);

          tween.delay(3000);

          tween.onUpdate(()=> {

          startPositions.needsUpdate=true;

          });

          tween.start();

          }

          每個(gè)點(diǎn)都有 x、y、z 坐標(biāo),也就是下標(biāo)為 i3、i3+1、i*3+2 的值,我們指定從群星的起始位置運(yùn)動(dòng)到 0,0,0 的位置。


          然后指定了時(shí)間函數(shù)為加速(Easing.Exponential.In),3000 ms 后開始執(zhí)行動(dòng)畫。


          每一幀渲染的時(shí)候要調(diào)用下 Tween.update 來計(jì)算最新的值:


          function render() {

          TWEEN.update();

          renderer.render(scene, camera);

          scene.rotation.y +=0.001;


          requestAnimationFrame(render);

          }

          每一幀在繪制的時(shí)候都會(huì)調(diào)用 onUpdate 的回調(diào)函數(shù),我們?cè)诨卣{(diào)函數(shù)里把 positions 的 needsUpdate 設(shè)置為 true,就是告訴 tween.js 在這一幀要更新為新的數(shù)值再渲染了。


          第一個(gè)粒子動(dòng)畫完成!


          來看下效果(我把這個(gè)效果叫做萬象天引):


          所有的星星粒子都集中到了一個(gè)點(diǎn),這就是粒子動(dòng)畫典型的打碎重組感。


          接下來,只要把粒子運(yùn)動(dòng)到福字的頂點(diǎn)就是我們要做的“群星送福”效果了。


          福字模型的頂點(diǎn)肯定不能隨機(jī),自己畫也不現(xiàn)實(shí),這種一般都是在建模軟件里畫好,然后導(dǎo)入到 Three.js 來渲染,


          我找了這樣一個(gè)福字的 3D 模型:


          圖片

          模型是 fbx 格式的,使用 FBXLoader 加載:


          const loader=new THREE.FBXLoader();

          loader.load('./obj/fu.fbx', function (object) {

          const destPosition=object.children[0].geometry.getAttribute('position');


          });

          回調(diào)參數(shù)就是從 fbx 模型加載的 3D 物體,它是一個(gè) Group(多個(gè) 3D 物體的集合),取出第 0 個(gè)元素的 geometry 屬性,就是對(duì)應(yīng)的幾何體。


          這樣,我們就拿到了目標(biāo)的頂點(diǎn)位置。


          把粒子動(dòng)畫的結(jié)束位置改為福字的頂點(diǎn)就可以了:


          const cur=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());

          如果開始頂點(diǎn)位置比較多,超過的部分從 0 的位置再來,所以要取余。


          大功告成!


          這就是我們想要的粒子效果:


          完整代碼上傳到了 github:https://github.com/QuarkGluonPlasma/threejs-exercize


          也在這里貼一份:


          <!DOCTYPE html>

          <html lang="en">

          <head>

          <meta charset="UTF-8">

          <meta name="viewport" content="width=device-width, initial-scale=1.0">

          <title></title>

          <style>

          body {

          margin: 0;

          }

          </style>

          <script src="./js/three.js"></script>

          <script src="./js/tween.js"></script>

          <script src="./js/FontLoader.js"></script>

          <script src="./js/TextGeometry.js"></script>

          <script src="./js/FBXLoader.js"></script>

          <script src="./js/fflate.js"></script>

          </head>

          <body>

          <script>

          const width=window.innerWidth;

          const height=window.innerHeight;

          const camera=new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);


          const scene=new THREE.Scene();

          const renderer=new THREE.WebGLRenderer();


          camera.position.set(100, 0, 400);

          camera.lookAt(scene.position);


          renderer.setSize(width, height);

          document.body.appendChild(renderer.domElement)


          function create() {

          const vertices=[];

          for ( let i=0; i < 30000; i ++ ) {

          const x=THREE.MathUtils.randFloatSpread( 2000 );

          const y=THREE.MathUtils.randFloatSpread( 2000 );

          const z=THREE.MathUtils.randFloatSpread( 2000 );

          vertices.push( x, y, z );

          }

          const geometry=new THREE.BufferGeometry();

          geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );


          const star=new THREE.TextureLoader().load('img/star.png');

          const material=new THREE.PointsMaterial( { size: 10, map: star });


          const points=new THREE.Points( geometry, material );

          points.translateY(-100);

          scene.add(points);


          const loader=new THREE.FBXLoader();

          loader.load('./obj/fu.fbx', function (object) {

          const startPositions=geometry.getAttribute('position');

          const destPosition=object.children[0].geometry.getAttribute('position')

          for(let i=0; i< startPositions.count; i++) {

          const tween=new TWEEN.Tween(startPositions.array);

          const cur=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());

          tween.easing(TWEEN.Easing.Exponential.In);

          tween.delay(3000);


          tween.start();


          tween.onUpdate(()=> {

          startPositions.needsUpdate=true;

          });

          }

          } );

          }


          function render() {

          TWEEN.update();

          renderer.render(scene, camera);

          scene.rotation.y +=0.001;


          requestAnimationFrame(render);

          }

          create();

          render();

          </script>

          </body>

          </html>

          總結(jié)

          粒子動(dòng)畫是組成物體的基本單位的運(yùn)動(dòng),對(duì) 3D 來說就是頂點(diǎn)的運(yùn)動(dòng)。


          我們要實(shí)現(xiàn)“群星送福”的粒子動(dòng)畫,也就是從群星的頂點(diǎn)運(yùn)動(dòng)到福字的頂點(diǎn)。


          群星的頂點(diǎn)可以隨機(jī)生成,使用 BufferGeometry 創(chuàng)建對(duì)應(yīng)的幾何體。福字則是加載創(chuàng)建好的 3D 模型,拿到其中的頂點(diǎn)位置。


          有了開始、結(jié)束位置,就可以實(shí)現(xiàn)粒子動(dòng)畫了,過程中的 x、y、z 值使用動(dòng)畫庫 Tween.js 來計(jì)算,可以指定加速、減速等時(shí)間函數(shù)。


          粒子動(dòng)畫有種打碎重組的感覺,可以用來做一些很炫的效果。理解了什么是粒子動(dòng)畫、動(dòng)的是什么,就算是初步掌握了。


          我摘下漫天繁星,想給大家送一份福氣,新的一年一起加油!

          不是還蠻酷的呢?利用周末時(shí)間我們來學(xué)習(xí)并實(shí)現(xiàn)一下,本文我們就來一點(diǎn)一點(diǎn)分析怎么實(shí)現(xiàn)它!


          分析


          首先我們看看這個(gè)效果具體有哪些要點(diǎn)。首先,這么炫酷的效果肯定是要用到 Canvas 了,每個(gè)星星可以看作為一個(gè)粒子,因此,整個(gè)效果實(shí)際上就是粒子系統(tǒng)了。此外,我們可以發(fā)現(xiàn)每個(gè)粒子之間是相互連接的,只不過離的近的粒子之間的連線較粗且透明度較低,而離的遠(yuǎn)的則相反。


          開始 Coding


          HTML 部分


          這部分我就簡(jiǎn)單放了一個(gè) 標(biāo)簽,設(shè)置樣式使其填充全屏。


          <canvas height="620" width="1360" id="canvas" style="position: absolute; height: 100%;"/>


          然后為了讓所有元素沒有間距和內(nèi)部,我還加了一條全局樣式:


          * {

          margin: 0;

          padding: 0;

          }


          JavaScript 部分


          下面我們來寫核心的代碼。首先我們要得到那個(gè) canvas 并得到繪制上下文:


          var canvasEl=document.getElementById('canvas');

          var ctx=canvasEl.getContext('2d');

          var mousePos=[0, 0];


          緊接著我們聲明兩個(gè)變量,分別用于存儲(chǔ)“星星”和邊:


          var nodes=[];

          var edges=[];


          下一步,我們做些準(zhǔn)備工作,就是讓畫布在窗口大小發(fā)生變化時(shí)重新繪制,并且調(diào)整自身分辨率:


          window.onresize=function () {

          canvasEl.width=document.body.clientWidth;

          canvasEl.height=canvasEl.clientHeight;

          if (nodes.length==0) {

          constructNodes();

          }

          render();

          };

          window.onresize(); // trigger the event manually.


          我們?cè)诘谝淮涡薷拇笮『髽?gòu)建了所有節(jié)點(diǎn),這里就要用到下一個(gè)函數(shù)(constructNodes)了


          這個(gè)函數(shù)中我們隨機(jī)創(chuàng)建幾個(gè)點(diǎn),我們用字典對(duì)象的方式存儲(chǔ)這些點(diǎn)的各個(gè)信息:


          function constructNodes() {

          for (var i=0; i < 100; i++) {

          var node={

          drivenByMouse: i==0,

          x: Math.random() * canvasEl.width,

          y: Math.random() * canvasEl.height,

          vx: Math.random() * 1 - 0.5,

          vy: Math.random() * 1 - 0.5,

          radius: Math.random() > 0.9 ? 3 + Math.random() * 3 : 1 + Math.random() * 3

          };

          nodes.push(node);

          }

          nodes.forEach(function (e) {

          nodes.forEach(function (e2) {

          if (e==e2) {

          return;

          }

          var edge={

          from: e,

          to: e2

          }

          addEdge(edge);

          });

          });

          }


          為了實(shí)現(xiàn)后面一個(gè)更炫酷的效果,我給第一個(gè)點(diǎn)加了一個(gè) drivenByMouse 屬性,這個(gè)點(diǎn)的位置不會(huì)被粒子系統(tǒng)管理,也不會(huì)繪制出來,但是它會(huì)與其他點(diǎn)連線,這樣就實(shí)現(xiàn)了鼠標(biāo)跟隨的效果了。


          這里稍微解釋一下 radius 屬性的取值,我希望讓絕大部分點(diǎn)都是小半徑的,而極少數(shù)的點(diǎn)半徑比較大,所以我這里用了一點(diǎn)小 tricky,就是用概率控制點(diǎn)的半徑取值,不斷調(diào)整這個(gè)概率閾值就能獲取期待的半徑隨機(jī)分布。


          點(diǎn)都構(gòu)建完畢了,就要構(gòu)建點(diǎn)與點(diǎn)之間的連線了,我們用到雙重遍歷,把兩個(gè)點(diǎn)捆綁成一組,放到 edges 數(shù)組中。注意這里我用了另外一個(gè)函數(shù)來完成這件事,而沒有直接用 edges.push() ,為什么?


          假設(shè)我們之前連接了 A、B兩點(diǎn),也就是外側(cè)循環(huán)是A,內(nèi)側(cè)循環(huán)是B,那么在下一次循環(huán)中,外側(cè)為B,內(nèi)側(cè)為A,是不是也會(huì)創(chuàng)建一條邊呢?而實(shí)際上,這兩個(gè)邊除了方向不一樣以外是完全一樣的,這完全沒有必要而且占用資源。因此我們?cè)?addEdge 函數(shù)中進(jìn)行一個(gè)判斷:


          function addEdge(edge) {

          var ignore=false;

          edges.forEach(function (e) {

          if (e.from==edge.from & e.to==edge.to) {

          ignore=true;

          }

          if (e.to==edge.from & e.from==edge.to) {

          ignore=true;

          }

          });

          if (!ignore) {

          edges.push(edge);

          }

          }


          至此,我們的準(zhǔn)備工作就完畢了,下面我們要讓點(diǎn)動(dòng)起來:


          function step() {

          nodes.forEach(function (e) {

          if (e.drivenByMouse) {

          return;

          }

          e.x +=e.vx;

          e.y +=e.vy;

          function clamp(min, max, value) {

          if (value > max) {

          return max;

          } else if (value < min) {

          return min;

          } else {

          return value;

          }

          }

          if (e.x <=0 || e.x >=canvasEl.width) {

          e.vx *=-1;

          e.x=clamp(0, canvasEl.width, e.x)

          }

          if (e.y <=0 || e.y >=canvasEl.height) {

          e.vy *=-1;

          e.y=clamp(0, canvasEl.height, e.y)

          }

          });

          adjustNodeDrivenByMouse();

          render();

          window.requestAnimationFrame(step);

          }

          function adjustNodeDrivenByMouse() {

          nodes[0].x +=(mousePos[0] - nodes[0].x) / easingFactor;

          nodes[0].y +=(mousePos[1] - nodes[0].y) / easingFactor;

          }


          看到這么一大段代碼不要害怕,其實(shí)做的事情很簡(jiǎn)單。這是粒子系統(tǒng)的核心,就是遍歷粒子,并且更新其狀態(tài)。更新的公式就是


          v=v + a

          s=s + v


          a是加速度,v是速度,s是位移。由于我們這里不涉及加速度,所以就不寫了。然后我們需要作一個(gè)邊緣的碰撞檢測(cè),不然我們的“星星”都無拘無束地一點(diǎn)點(diǎn)飛~走~了~。邊緣碰撞后的處理方式就是讓速度矢量反轉(zhuǎn),這樣粒子就會(huì)“掉頭”回來。


          還記得我們需要做的鼠標(biāo)跟隨嗎?也在這處理,我們讓第一個(gè)點(diǎn)的位置一點(diǎn)一點(diǎn)移動(dòng)到鼠標(biāo)的位置,下面這個(gè)公式很有意思,可以輕松實(shí)現(xiàn)緩動(dòng):


          x=x + (t - x) / factor


          其中 factor 是緩動(dòng)因子,t 是最終位置,x 是當(dāng)前位置。至于這個(gè)公式的解釋還有個(gè)交互大神 Bret Victor 在他的演講中提到過,視頻做的非常好,有條(ti)件(zi)大家一定要看看: Bret Victor – Stop Drawing Dead Fish


          好了,回到主題。我們?cè)谏厦娴暮瘮?shù)中處理完了一幀中的數(shù)據(jù),我們要讓整個(gè)粒子系統(tǒng)連續(xù)地運(yùn)轉(zhuǎn)起來就需要一個(gè)timer了,但是十分不提倡大家使用 setInterval,而是盡可能使用 requestAnimationFrame,它能保證你的幀率鎖定在


          剩下的就是繪制啦:


          function render() {

          ctx.fillStyle=backgroundColor;

          ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);

          edges.forEach(function (e) {

          var l=lengthOfEdge(e);

          var threshold=canvasEl.width / 8;

          if (l > threshold) {

          return;

          }

          ctx.strokeStyle=edgeColor;

          ctx.lineWidth=(1.0 - l / threshold) * 2.5;

          ctx.globalAlpha=1.0 - l / threshold;

          ctx.beginPath();

          ctx.moveTo(e.from.x, e.from.y);

          ctx.lineTo(e.to.x, e.to.y);

          ctx.stroke();

          });

          ctx.globalAlpha=1.0;

          nodes.forEach(function (e) {

          if (e.drivenByMouse) {

          return;

          }

          ctx.fillStyle=nodeColor;

          ctx.beginPath();

          ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);

          ctx.fill();

          });

          }


          常規(guī)的 Canvas 繪圖操作,注意 beginPath 一定要調(diào)用,不然你的線就全部穿在一起了… 需要說明的是,在繪制邊的時(shí)候,我們先要計(jì)算兩點(diǎn)距離,然后根據(jù)一個(gè)閾值來判斷是否要繪制這條邊,這樣我們才能實(shí)現(xiàn)距離遠(yuǎn)的點(diǎn)之間連線不可見的效果。


          到這里,我們的整個(gè)效果就完成了。如果不明白大家也可以去GitHub項(xiàng)目: CyandevToys / ParticleWeb去看完整的源碼。Have fun!!

          源自:http://www.jianshu.com/p/f5c0f9c4bc39

          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請(qǐng)聯(lián)系小編刪除。


          主站蜘蛛池模板: 亚洲视频一区二区| 久久中文字幕一区二区| 日本一区二区三区免费高清在线| 国产精品无码一区二区三区免费 | 日韩精品一区二区三区色欲AV | 国产综合无码一区二区色蜜蜜| 日本免费一区二区三区最新vr| 日韩精品一区二区三区中文字幕 | 亚洲熟妇av一区二区三区漫画| 亚洲国产一区二区三区青草影视| 精品乱码一区二区三区四区| 国产一区韩国女主播| 红桃AV一区二区三区在线无码AV| 午夜福利av无码一区二区 | 一区二区三区免费在线视频 | 2018高清国产一区二区三区 | 香蕉免费看一区二区三区| 国产a∨精品一区二区三区不卡| 国产香蕉一区二区在线网站| 色精品一区二区三区| 色窝窝无码一区二区三区色欲 | 亚洲视频一区二区三区四区| 波多野结衣中文字幕一区| 国产人妖视频一区二区| 无码视频一区二区三区| 国产一区二区视频在线观看| 51视频国产精品一区二区| 久久久人妻精品无码一区| 国产乱码精品一区二区三区中| 亚洲国产专区一区| 亚洲熟妇AV一区二区三区浪潮| 精品乱子伦一区二区三区高清免费播放| 久久综合九九亚洲一区| 精品视频一区二区| 亚洲国产AV一区二区三区四区 | 日本一区二区三区免费高清| 国产色情一区二区三区在线播放 | 亚洲乱码国产一区网址| 精品人妻无码一区二区三区蜜桃一| 国产午夜精品一区二区三区不卡| 亚洲av无码一区二区三区在线播放|