通過幾天的學習,對openGL、shader有了一個大致的了解。
回到學習的初衷吧,在基於pixi.js重構D3項目的時候,因為精靈層級的問題,我得按照一定的先后順序將不同類別的精靈添加到場景中去。
例如:
針對人物關系的關系圖譜,所有的關系線必須要在所有的任務面板下面,但是移動人物面板的時候,與之關聯的關系線也要重新繪制;
所以刪除精靈之后再添加精靈使得層級增加的做法就有點不適用了(這會導致當前操作的關系線的層級提升,很顯然,這不是我們所想要的)。
因此,我們每次操作,都要重新將所有的精靈(處理好新的位置之后),分批次添加到場景中,這就導致出現下面的代碼:
人物面板是一類精靈,關系線又是一類精靈,所以我們要用到兩個for循環,並且順序還得講究(先for循環關系線,再for循環人物面板);
這樣一來,如果頁面中有10000個人物面板(相對應的就有10000條關系線),那么就要for循環10000+10000=20000次,針對拖拽操作,每觸發一次移動就要for循環20000次,
經過測試,由於精靈數組是存於內存中的,for循環的時候很方便,但是對CPU的消耗很高!
當時,遇到這個問題的時候,朋友說可以將這部分的for循環也讓GPU去執行,然后就看到了shader,進而學習了openGL;
學完之后,發現着色器程序的渲染模式可以用四個字概括:逐點繪制。確實解決了在CPU上執行for循環的問題。但是一個錯綜復雜的人物關系圖譜,要用着色器去繪制的話(咱先不討論shader加載紋理的問題),是不是就失去了使用pixi.js的意義,而且這也將使得開發工作變得更加復雜。
所以,我決定,鑒於關系圖譜項目的特殊性,使用依次for循環的方式來處理精靈層級的問題。
分析完問題之后,我們還是要看看,在pixi中如何使用我們自定義的着色器程序。
一、pixi_shaders
百度雲鏈接:https://pan.baidu.com/s/16m2BX5qKXr5Wm-J-68eXGA
提取碼:j5t9
效果圖:
3個iframe,3個片元着色器效果。
二、着色器代碼分析
第三個iframe對應的shader,效果是顏色不斷變化
let width = window.innerWidth; let height = window.innerHeight; let app = new PIXI.Application(width, height); document.body.appendChild(app.view); let shaderFrag = ` precision mediump float; uniform vec3 iResolution; uniform float iTime; void main() { // Normalized pixel coordinates (from 0 to 1) vec2 uv = gl_FragCoord.xy/iResolution.xy; // Time varying pixel color vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); // Output to screen gl_FragColor = vec4(col,1.0); } `; let container = new PIXI.Container(); container.filterArea = app.screen; app.stage.addChild(container); let filter = new PIXI.Filter(null, shaderFrag); filter.uniforms.iResolution = [width, height, 1.0]; filter.uniforms.iTime = 1.0; container.filters = [filter]; // Animate the filter app.ticker.add(function(delta) { filter.uniforms.iTime += 0.1; });
前面我們談到,着色器的渲染模式是逐點繪制,所以我們只要定義任意一點繪制方式就可以了,因為他會自動將我們的着色器代碼應用到指定區域內的每個像素點上。
繪制方式,這個概念有點抽象了,實際上就是顏色,每個像素點的顏色!!!
我們來看上面的着色器代碼:
let shaderFrag = ` precision mediump float; uniform vec3 iResolution; uniform float iTime; void main() { // Normalized pixel coordinates (from 0 to 1) vec2 uv = gl_FragCoord.xy/iResolution.xy; // Time varying pixel color vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); // Output to screen gl_FragColor = vec4(col,1.0); } `;
這個着色器接收由外面傳入的兩個參數:三維坐標iResolution和隨着時間遞增的時間參數iTime;
let filter = new PIXI.Filter(null, shaderFrag); filter.uniforms.iResolution = [width, height, 1.0]; filter.uniforms.iTime = 1.0; container.filters = [filter];
然后着色器程序先將坐標進行轉換(轉換為pixi適用的坐標),
然后根據轉換后的坐標(向量)和時間參數計算出三維顏色(RGB),着色器程序輸出的顏色常量gl_FragColor是四維向量(RGBA),所以我們人為地給他加上alpha通道值。
這樣一來,單個像素點輸出的顏色就確定了,然后逐點繪制,屏幕上所有的點的顏色就渲染出來了。
此外,加上一個pixi的動畫,通過改變iTime參數的值,實現改變單個像素點輸出的顏色值,這樣,我們看上去就是顏色不斷變化的屏幕了。
app.ticker.add(function(delta) { filter.uniforms.iTime += 0.1; });