three.js+shader 制作粒子飛線,小蝌蚪那種朴则。

學(xué)習(xí)three.js四個月了权纤,最近終于利用shader粒子做出了我心心念念的蝌蚪狀飛線,這個網(wǎng)上也是查不到什么資料的乌妒,2Dcanvas的可以查得到汹想,3D的都藏著掖著很難找的到相關(guān)案例,最近我通過騰訊一個小姐姐發(fā)布幾個three.js 案例受到了一點啟發(fā)撤蚊,終于花了一個晚上做了出來古掏,也是第一次在three.js中使用shader。

下面是效果圖:

先說說思路侦啸,第一步是制作一個蝌蚪狀的粒子束槽唾,其實很簡單就是讓粒子一個一個從小到大排列就好,這部分主要利用shader處理光涂,代碼如下:

const vs:string = `

? ? ? attribute float size; // 頂點尺寸

? ? ? attribute vec4 colors; //頂點顏色

? ? ? varying float opacity; // 控制透明度

? ? ? varying vec3 vexColor; // 頂點顏色

? ? ? void main(){

? ? ? ? ? vexColor.x = colors.r;

? ? ? ? ? vexColor.y = colors.g;

? ? ? ? ? vexColor.z = colors.b;

? ? ? ? ? //w分量為透明度

? ? ? ? ? opacity = colors.w;

? ? ? ? ? vec4 mvPosition = modelViewMatrix * vec4(position,1.0); //?這里模型矩陣庞萍,坐標向量,和投影矩陣都是three給你注入的好像忘闻。

? ? ? ? ? gl_PointSize = size;

? ? ? ? ? gl_Position = projectionMatrix * mvPosition;

? ? ? }

`;

然后就是配置數(shù)據(jù)源钝计,數(shù)據(jù)源可以利用three.js 給的curve3組件中的getPoints取得,代碼如下

this.spline = new THREE.CatmullRomCurve3(vecs);

? ? this.pointNum = num;

? ? this.distance = this.spline.getLength();

? ? //初始化粒子

? ? this.points = this.spline.getPoints(num);

? ? const colorsLen = this.points.length * 4;

? ? const sizeLen = this.points.length;

? ? const colors:Float32Array = new Float32Array(colorsLen);

? ? const sizes:Float32Array = new Float32Array(sizeLen);

? ? this.geometry = new THREE.BufferGeometry().setFromPoints(this.points);

? ? for(let i=0,z=0;i<colorsLen;i+=4,z++){

? ? ? //color

? ? ? colors[i] = color.r;

? ? ? colors[i+1] = color.g;

? ? ? colors[i+2] = color.b;

? ? ? // opacity

? ? ? colors[i+3] = (i+3)/sizeLen;

? ? ? // size從小到大

? ? ? sizes[z] =size*(z/sizeLen);

? ? };

? ? this.geometry.addAttribute('colors',new THREE.BufferAttribute(colors,4));

? ? this.geometry.addAttribute('size',new THREE.BufferAttribute(sizes,1));


第二部分就是如何讓粒子運動起來齐佳,這里我借助了tween.js葵蒂,整體思路就是取一段固定的粒子數(shù)目+加上Curve3的getPointAt取點函數(shù),不斷的取出固定數(shù)目并不斷前移的坐標重虑,直接修改bufferGeometry的position數(shù)據(jù)即可践付。


下面貼出完整代碼:

import * as THREE from "three";

import TWEEN from "@tweenjs/tween.js";

const fs:string = `

? ? ? uniform sampler2D texture;

? ? ? varying float opacity;

? ? ? varying vec3 vexColor;

? ? ? void main(){

? ? ? ? ? gl_FragColor = vec4(vexColor,opacity);

? ? ? ? ? gl_FragColor = gl_FragColor * texture2D(texture,gl_PointCoord);

? ? ? }? ? ?

`;

const vs:string = `

? ? ? attribute float size;

? ? ? attribute vec4 colors;

? ? ? varying float opacity;

? ? ? varying vec3 vexColor;

? ? ? void main(){

? ? ? ? ? vexColor.x = colors.r;

? ? ? ? ? vexColor.y = colors.g;

? ? ? ? ? vexColor.z = colors.b;

? ? ? ? ? //w分量為透明度

? ? ? ? ? opacity = colors.w;

? ? ? ? ? vec4 mvPosition = modelViewMatrix * vec4(position,1.0);

? ? ? ? ? gl_PointSize = size;

? ? ? ? ? gl_Position = projectionMatrix * mvPosition;

? ? ? }

`;

/**

* 粒子飛線

*/

export default class PointsFlyLine{

? //粒子位置

? geometry: THREE.BufferGeometry;

? //曲線

? spline: THREE.CatmullRomCurve3;

? //粒子系統(tǒng)

? particleSystem: THREE.Points;

? //粒子數(shù)目

? pointNum:number;

? //粒子間的總距離

? distance: number;

? points: THREE.Vector3[];

? tween: any;

? /**

? * 創(chuàng)建粒子系統(tǒng)

? * @param points 粒子

? * @param size 粒子大小

? * @param num 粒子數(shù)目

? * @param color 粒子顏色

? */

? constructor({ vecs, num, size, color }: { vecs: THREE.Vector3[]; num: number; size: number; color: THREE.Color; }){

? ? this.spline = new THREE.CatmullRomCurve3(vecs);

? ? this.pointNum = num;

? ? this.distance = this.spline.getLength();

? ? //初始化粒子

? ? this.points = this.spline.getPoints(num);

? ? const colorsLen = this.points.length * 4;

? ? const sizeLen = this.points.length;

? ? const colors:Float32Array = new Float32Array(colorsLen);

? ? const sizes:Float32Array = new Float32Array(sizeLen);

? ? this.geometry = new THREE.BufferGeometry().setFromPoints(this.points);

? ? for(let i=0,z=0;i<colorsLen;i+=4,z++){

? ? ? //color

? ? ? colors[i] = color.r;

? ? ? colors[i+1] = color.g;

? ? ? colors[i+2] = color.b;

? ? ? // opacity

? ? ? colors[i+3] = (i+3)/sizeLen;

? ? ? // size從小到大

? ? ? sizes[z] =size*(z/sizeLen);

? ? };

? ? this.geometry.addAttribute('colors',new THREE.BufferAttribute(colors,4));

? ? this.geometry.addAttribute('size',new THREE.BufferAttribute(sizes,1));

? ? const uniforms:object = {

? ? ? texture: {

? ? ? ? value: new THREE.CanvasTexture(this.createSpriteCanvas(size)),

? ? ? }

? ? };

? ? const shaderMaterial:THREE.ShaderMaterial = new THREE.ShaderMaterial({

? ? ? uniforms,

? ? ? vertexShader:vs,

? ? ? fragmentShader:fs,

? ? ? transparent:true,

? ? ? depthTest:false

? ? });

? ? this.particleSystem = new THREE.Points(this.geometry,shaderMaterial);

? }

? //飛線開始

? start(){

? ? const max = this.distance*10;

? ? const end:number = this.pointNum;

? ? const m = {start:0,end};

? ? this.tween = this.tweenAnimate(m,{start:max-end,end:max},2000,null,()=>{

? ? ? let pointArr:number[] = [];

? ? ? let s = Math.round(m.start),e = Math.floor(m.end);

? ? ? for (let i = s; i <= e && i<=max; i++) {

? ? ? ? pointArr = pointArr.concat(this.spline.getPointAt(i/max).toArray());

? ? ? }

? ? ? this.geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(pointArr),3);

? ? });

? ? this.tween.repeat(Infinity).start();

? }

? stop(){

? ? this.tween.stop();

? }

? tweenAnimate(current:object, target:object, interval:number, animation?:TWEEN.Easing, onUpdate?:Function, complete?:Function) {

? ? var animate = animation ? animation : TWEEN.Easing.Linear.None;

? ? let tween = new TWEEN.Tween(current).to(target, interval).easing(animate);

? ? onUpdate && tween.onUpdate(() => onUpdate());

? ? complete && tween.onComplete(() => complete());

? ? return tween;

? }

? //創(chuàng)建圓形精靈貼圖

? createSpriteCanvas(size:number){

? ? const canvas = document.createElement('canvas');

? ? canvas.width = canvas.height = size;

? ? const context = canvas.getContext('2d');

? ? if(context!=null){

? ? ? context.fillStyle='rgba(255,255,255,.0)';

? ? ? context.beginPath();

? ? ? context.arc(size/2,size/2,size/2,0,Math.PI*2);

? ? ? context.fillStyle = 'white';

? ? ? context.fill();

? ? ? context.closePath();

? ? }

? ? return canvas;

? }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缺厉,隨后出現(xiàn)的幾起案子永高,更是在濱河造成了極大的恐慌隧土,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件命爬,死亡現(xiàn)場離奇詭異曹傀,居然都是意外死亡,警方通過查閱死者的電腦和手機饲宛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門皆愉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艇抠,你說我怎么就攤上這事幕庐。” “怎么了家淤?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵异剥,是天一觀的道長。 經(jīng)常有香客問我絮重,道長冤寿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任青伤,我火速辦了婚禮督怜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狠角。我一直安慰自己亮蛔,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布擎厢。 她就那樣靜靜地躺著,像睡著了一般辣吃。 火紅的嫁衣襯著肌膚如雪动遭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天神得,我揣著相機與錄音厘惦,去河邊找鬼。 笑死哩簿,一個胖子當著我的面吹牛宵蕉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播节榜,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼羡玛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宗苍?” 一聲冷哼從身側(cè)響起稼稿,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤薄榛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后让歼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敞恋,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年谋右,在試婚紗的時候發(fā)現(xiàn)自己被綠了硬猫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡改执,死狀恐怖啸蜜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情天梧,我是刑警寧澤盔性,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站呢岗,受9級特大地震影響冕香,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜后豫,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一悉尾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挫酿,春花似錦构眯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至葱弟,卻和暖如春壹店,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芝加。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工硅卢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藏杖。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓将塑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蝌麸。 傳聞我的和親對象是個殘疾皇子点寥,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容