three.js粒子效果(分別基於CPU&GPU實現)


前段時間做了一個基於CPU和GPU對比的粒子效果丟在學習WebGL的群里,技術上沒有多作講解,有同學反饋看不太懂GPU版本,干脆開一篇文章,重點講解基於GPU開發的版本。

一、概況

 

廢話不多說,先丟上demo,用移動設備更能明顯感覺性能差異。

維護粒子位移顏色尺寸
GPU版本  CPU版本

維護粒子位移
GPU版本  CPU版本

 

結論:
同時需要維護多種粒子特征變化時,GPU有明顯優勢。
只是維護粒子位移時,GPU版本稍流暢,但優勢並不明顯。
當然,這還得具體到設備,一些中低端Android機器,GPU太渣,不如CPU計算。

 

二、技術實現

three.js中,粒子效果的實現方式大概分為三種:
1、Javascript直接計算粒子的狀態變化,即基於CPU實現;
2、Javascript通知頂點着色器粒子的生命周期,由頂點着色器運行,即基於GPU實現;
3、粒子生成與狀態維護全部由片元着色器負責,即屏幕特效,同樣是基於GPU中實現。
第3種方式本文暫不介紹。

 

2.1、基於CPU實現

維護位移、顏色、尺寸:
http://tgideas.qq.com/2017/three/shader/particle-gpu/cpu.html
維護位移:
http://tgideas.qq.com/2017/three/shader/particle-gpu/gpu-position.html

步驟1&2:
首先加載由三維軟件制作的幾何體,然后生成粒子系統 。

var material = new THREE.PointsMaterial({size:4, color:0xff0000});
var particleSystem = new THREE.Points(geometry , material);

從代碼中可以看出,材質是針對整介粒子系統設置的,所以只能維護粒子位移。
如果要維護粒子顏色、尺寸呢?
我們必須為每個粒子設置不同的材質,由此也造成不小的性能損耗 。
 
步驟3:
使用Tween修改所有頂點位置。

var tween = new TWEEN.Tween(pos).to({val: 0}, 2000).easing(TWEEN.Easing.Quadratic.InOut).delay(1000).onUpdate(callback);
function callback(){
	var val = this.val;
	var particles = particleSystem.geometry.vertices;
	for(var i = 0; i < particles.length; i++) {
		var pos = particles[i];
		pos.x = position1[i].x * val + position2[i].x * (1-val);
		pos.y = position1[i].y * val + position2[i].y * (1-val);
		pos.z = position1[i].z * val + position2[i].z * (1-val);
	}
	particleSystem.geometry.verticesNeedUpdate = true;
}

從代碼中可以看出,粒子的狀態都是通過Javascript,由CPU來計算。

 

2.2、基於GPU實現

維護粒子位移、顏色、尺寸:
http://tgideas.qq.com/2017/three/shader/particle-gpu/gpu.html

對比CPU實現流程圖,我們會發現,Tween並不直接計算所有頂點位置,而是只通知動畫運行時間,由頂點着色器來完成具體運算。
既然運算部分在頂點着色器,那么,需要我們自己書寫着色器(opengl es),所以我們選用three.js中的ShaderMaterial。

步驟1:
首先生成粒子系統:

var uniforms = {
	texture:{value: new THREE.TextureLoader().load( "dot.png")},
	val: {value: 1.0}
};
var shaderMaterial = new THREE.ShaderMaterial({
	uniforms:     uniforms,
	vertexShader:   document.getElementById('vertexshader').textContent,
	fragmentShader: document.getElementById('fragmentshader').textContent,
	blending:       THREE.AdditiveBlending,
	depthTest:      false,
	transparent:    true
});
particleSystem = new THREE.Points(moreObj, shaderMaterial);

uniforms是連接javascript與着色器的通道。
uniforms.val 即由tween來維護的動畫運行值。
vertexShader和fragmentShader,即我們要定義的頂點着色器,和片元着色器,它們負責具體的粒子狀態的運算,我們定義在網頁中。

步驟2:
定義頂點着色器:

attribute float size; // 粒子尺寸
attribute vec3 position2; // 目標頂點位置
uniform float val; // 動畫運行時間
varying vec3 vPos; // 將頂點位置傳輸給片元着色器

void main() {
    // 計算粒子位置
    vPos.x = position.x * val + position2.x * (1.-val);
    vPos.y = position.y* val + position2.y * (1.-val);
    vPos.z = position.z* val + position2.z * (1.-val);
    // 坐標轉換
    vec4 mvPosition = modelViewMatrix * vec4( vPos, 1.0 );
    gl_PointSize = size * ( 300.0 / -mvPosition.z );
    gl_Position = projectionMatrix * mvPosition;

}

 

three.js內置,自動傳遞給頂點着色器的變量:
attribute position - 頂點坐標
mat4 modelViewMatrix - 模型+視圖矩陣
mat4 projectionMatrix - 投影矩陣

定義片元着色器:

uniform sampler2D texture;
varying vec3 vPos;

void main() {
    // 計算粒子顏色,通過位置
    vec3 vColor = vec3(1.0, 0., 0.);
    vColor.r = vPos.z/50.;
    vColor.g = vPos.y/50.;
    vColor.b = vPos.x/50.;

    gl_FragColor = vec4(vColor, 1.0 );
    // 頂點貼圖
    gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );

}

  

步驟3:
負責維護粒子運行時間:

tween = new TWEEN.Tween(pos).to({val: 0}, 2000).onUpdate(callback);
function callback(){
	particleSystem.material.uniforms.val.value = this.val;
}

 

三、延伸閱讀

類THREE.Points做了什么?
其實真沒干什么,主要是申明它的type是Points。
當我們執行渲染時,WebGL會繪制Point,即調用gl.drawArrays(gl.POINTS…
而通常,比如type為Mesh時,three.js會調用gl.drawArrays(gl.TRIANGLES…

類THREE.PointsMaterial做了什么?
同樣,點材質也是three.js最簡單的類之一,相對於基類Material,它多做的事情只是傳遞了size,即點的尺寸這個值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM