純Shading Language繪製飛機火焰效果

上篇《純Shading Language繪製HTML5時鐘》體現了GLSL可編程性特色,但沒有體現GLSL可編程出各類酷炫效果的特色,今天咱們將用純Shading Language繪製火焰效果,並將其應用到《HT圖形組件設計之道(四)》飛行的飛機例子上。php

Screen Shot 2015-01-01 at 8.48.26 PM

火焰的例子我已發在 http://js.do/hightopo/fireball,其本質在繪製gl.POINTS的點類型時,經過在Fragment Shader在點區域內生成noise的噪聲用於繪製多種顏色效果,並將屢次不一樣噪聲算法生成的顏色進行疊加,同時噪聲的生成還依賴於time的時間參數,這樣最終融合成不錯的圓形火焰效果。html

採用gl.POINTS的繪製方式會受不一樣瀏覽器對點大小的限制,可經過gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)獲知瀏覽器支持的最小和最大範圍,通常也都有1~255或1~300的區間,因此也基本夠用於展現效果,http://js.do/hightopo/fireball的例子中52行中的float color = 3.0 – (3.*length(2.*p));其中的第一個3.0是火焰強度intensity參數,可經過改變此值達到改變火焰強度的效果,可在1.0~4.0範圍體驗從小火到大火的調節效果。node

Screen Shot 2015-01-01 at 8.31.49 PMScreen Shot 2015-01-01 at 8.32.42 PM

HT圖形組件設計之道(四)》文中的例子我將在飛機的尾部疊加該火焰效果,因爲考慮到自定義GLSL的複雜性,HT並未開放圖元自定義GLSL的功能,咱們將要採用的是在Graph3dView的上層再次疊加一個WebGL驅動的Canvas,火焰是繪製到這個上層的Canvas,所以和HT的Graph3dView徹底是鬆耦合,不會影響HT的3D組件自身的全部顯示和交互功能,這樣的應用有點相似《百度地圖與HT for Web結合的GIS網絡拓撲應用》中GIS地圖與HT的GraphView組件疊加效果。算法

Screen Shot 2014-10-08 at 7.45.25 PM

固然這樣疊加會致使火焰始終在最上層,沒法真實反映三維空間層次的問題,但做爲監控系統應用最關鍵的是展現重要指標,例如對於電信網管應用,當設備有告警冒泡呈現時,每每要求告警冒泡要呈如今最上層不要被其餘設備遮擋住,一樣若是真的飛機失火須要監控系統實時提示該告警信息時,確定也是須要該火焰不被遮擋,所以真實世界的層次瑕疵在這裏反而是合適的解決方案。編程

Screen Shot 2015-01-03 at 10.45.06 PM

疊加Canvas到Graph3dView比較容易,經過Graph3dView.getView().appendChild(canvas)加入,並在Graph3dView佈局時同時佈局Cavnas位置大小,火焰的位置咱們是這樣實現的,將一個隱藏的node節點host到飛機的尾部,這樣該節點會自定跟隨飛機飛行過程的位置變化,繪製時火焰時咱們經過Graph3dView#toViewPosition函數將node三維座標轉換成二維的屏幕Cavnas的座標,這還沒完咱們還得將屏幕座標轉換成WebGL驅動的Canvas的POINT點座標,相關代碼以下:canvas

function draw(){
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.clearColor(0.0, 0.0, 0.0, 0.0);
    gl.clear(gl.COLOR_BUFFER_BIT);     

    gl.uniform1f(gl.getUniformLocation(program, 'time'), (Date.now() - startTime)/1000 ); 
    gl.uniform1f(gl.getUniformLocation(program, 'intensity'), intensity ); 

    var vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    var position = g3d.toViewPosition(node.p3());                                
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
         position.x/g3d.getWidth()*2 - 1,
         1 - position.y/g3d.getHeight()*2
    ]), gl.STATIC_DRAW);
    var vertexLocation = gl.getAttribLocation(program, "aVertexPosition");
    gl.vertexAttribPointer(vertexLocation, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vertexLocation);                   
    gl.drawArrays(gl.POINTS, 0, 1);

    requestAnimationFrame(draw); 
}              

function resizeCanvas(){
    canvas.width = g3d.getWidth() * devicePixelRatio;
    canvas.style.width = g3d.getWidth() + 'px';  
    canvas.height = g3d.getHeight() * devicePixelRatio;
    canvas.style.height = g3d.getHeight() + 'px';                 
}

以上代碼咱們還傳入了intensity的強度參數,該參數咱們經過ht.Default.startAnim動畫函數,控制其值在0~4之間不斷來回變化,這樣可達到火焰有小變大來回變換的相似告警閃爍提示效果。還有var devicePixelRatio = window.devicePixelRatio;參數也是容易被忽略的細節,該值會根據設備的retina支持程度有不一樣的值,避免在高分辨率設備下出現鋸齒模糊的問題,固然經過devicePixelRatio增大canvas.width和canvas.height也是有內存繪製性能代價的,若是效果要求不是過高狀況下也能夠都採用1來處理,其實要求不過高的三維場景即便時retina爲3的iphone 6強制用devicePixelRatio爲1的方式也不會有太大問題,而且能節省內存提升繪製性能,某些低性能的終端某些狀況下甚至能夠再降級到小於1的值以犧牲效果換取性能。瀏覽器

千辛萬苦終於讓飛機飛出了我想要的效果(http://v.youku.com/v_show/id_XODYyMzU3MDg0.html),固然還有無數的細節能夠完善,例如能夠根據飛機離eye的距離動態改變POINT點的大小,或改造GLSL實現煙霧的粒子系統效果等等,但元旦假期結束了我明天還要上班,其餘可完善的地方留給讀者去想象了。網絡

Screen Shot 2015-01-03 at 11.18.30 PM

相關文章
相關標籤/搜索