前段時間作了一個基於CPU和GPU對比的粒子效果丟在學習WebGL的羣裏,技術上沒有多做講解,有同窗反饋看不太懂GPU版本,乾脆開一篇文章,重點講解基於GPU開發的版本。javascript
廢話很少說,先丟上demo,用移動設備更能明顯感受性能差別。html
結論:
同時須要維護多種粒子特徵變化時,GPU有明顯優點。
只是維護粒子位移時,GPU版本稍流暢,但優點並不明顯。
固然,這還得具體到設備,一些中低端Android機器,GPU太渣,不如CPU計算。性能
three.js中,粒子效果的實現方式大概分爲三種:
一、Javascript直接計算粒子的狀態變化,即基於CPU實現;
二、Javascript通知頂點着色器粒子的生命週期,由頂點着色器運行,即基於GPU實現;
三、粒子生成與狀態維護所有由片元着色器負責,即屏幕特效,一樣是基於GPU中實現。
第3種方式本文暫不介紹。學習
2.一、基於CPU實現動畫
維護位移、顏色、尺寸:
http://tgideas.qq.com/2017/three/shader/particle-gpu/cpu.html
維護位移:
http://tgideas.qq.com/2017/three/shader/particle-gpu/gpu-position.htmlthis
步驟1&2:
首先加載由三維軟件製做的幾何體,而後生成粒子系統 。idea
var material = new THREE.PointsMaterial({size:4, color:0xff0000}); var particleSystem = new THREE.Points(geometry , material);
從代碼中能夠看出,材質是針對整介粒子系統設置的,因此只能維護粒子位移。
若是要維護粒子顏色、尺寸呢?
咱們必須爲每一個粒子設置不一樣的材質,由此也形成不小的性能損耗 。
步驟3:
使用Tween修改全部頂點位置。spa
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.二、基於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,即點的尺寸這個值。