最近項目開發任務告一段落,恰好有時間整理這大半年的一些成果。使用html5時間還不久,對js的認識還不夠深刻。沒辦法,之前一直搞java,對js的一些語言特性和概念一時還轉換不過來。html
上一篇大數據呈現第一彈介紹了項目中作的一個彩虹爆炸圖,主要用了 html5的canvas的2d繪製技術。這一回我想介紹一下項目中的一個亮點技術:html5的3D,以及如何用它打造精美的3D機房監控系統。html5
下圖是客戶給找的一張的效果參考圖,但願機房至少能達到下面的3D效果。
java
懂的人都知道,這但是一張設計公司出的裝修效果圖啊,就算是用max建模,也須要大量的工做,況且咱但是程序員在作數據中心的可視化項目啊。。。強忍心中奔騰的萬千頭**馬,靜下心來思考,那就先從搭建一個webGL的場景開始吧。程序員
在html5裏面使用3D已經不是什麼高深技術,它的基礎是WebGL,一個OpenGL的瀏覽器子集,支持大部分主要3D功能接口。目前最新的瀏覽器都有比較好的支持,IE須要到11(是的,你沒有看錯)。web
要檢測你的瀏覽器是否支持webGL,可直接訪問網頁http://get.webgl.org/ 看是否能看到一個旋轉的立方體。若是能看到,說明你的瀏覽器支持webgGL,不然,能夠下一個最新的chrome試試吧。相對來講chrome對webGL的支持最好,效率也很優秀。chrome
要在瀏覽器裏面使用webGL,就要研究webGL相關的技術和用法。作3D應用並非一件輕鬆的事。就算最簡單的搭建一下webGL場景,也須要下面這些代碼:數據庫
var width = window.innerWidth; var height= window.innerHeight; var container = document.createElement( 'div' ); document.body.appendChild( container ); var webglcanvas = document.createElement('canvas'); container.appendChild(webglcanvas); var gl = webglcanvas.getContext("experimental-webgl"); function updateFrame () { gl.viewport ( 0, 0, width, height ); gl.clearColor(0.4, 0.4, 0.7, 1); gl.clear ( gl.COLOR_BUFFER_BIT ); setTimeout( function(){updateFrame()}, 20); } setTimeout( function(){ updateFrame(); }, 20);
和html同樣,須要先建立一個canvas元素,並得到其webgl上下文:json
var gl = webglcanvas.getContext("experimental-webgl");
canvas
而後在一個updateFrame
的函數中,像html5的2D context同樣,去繪製3D的內容。小程序
另外,要再起一個死循環,每隔**毫秒調用一次這個updateFrame
函數來重繪場景。和2D不一樣,3D場景裏面的變化是隨時隨地的,因此須要不停刷新,就像播放電影或視頻,靜止不動的畫面基本沒有,因此死循環刷新基本是必要的。不過實際項目使用中會有不少優化,儘可能作到「按需刷新」,節省cpu和移動設備電量。有感興趣的同窗,哥能夠單獨寫文章介紹。這段程序基本上什麼也沒作,就畫了一個靜止不動的區域,以下圖:
雖然看不見任何3D的內容,不過它已是一個最簡單的webgl程序了。咱們的3D機房,也就是在這上面不斷豐富而已。
要作項目,搭建下去工做量太大了,時間週期也不容許。使用第三方輔助工具是不可避免的,像Three.js, twaver.js都是選擇。這些工具均可以提供3D的基本對象和各類特效,固然這都不是最主要的,主要是如何利用它作出我想要的效果:好看。爲了不大量修改代碼,在項目裏作了一些封裝,即把原始3D的立方體等對象進行進一步封裝,讓一個json數據就能夠提供這些對象的定義。這樣使用起來就比較方便了。json大體結構以下:
var json={ objects: [{ name: '地板', … },{ … }], }
下面咱們逐一來看這些3D對象是怎麼進行美化的,過程可能稍顯囉嗦,跬步千里,此次的基礎打好了,之後的項目就手到擒來了。
第一個要作的,也是應該比較簡單的,就是地板對象。3D中,地板應該是一個有些厚度、帶上格子貼圖的薄薄立方體平面。所以我對通過封裝的立方體對象,用一段json對象定義以下:
{ name: '地板', type: 'cube', width: 1600, height: 10, depth: 1300, style: { 'm.color': '#BEC9BE', 'm.ambient': '#BEC9BE', } }
經過定義,建立了一個13米*16米的地板塊,這也是客戶小型機房的實際尺寸:
看起來有那麼點意思,就是顏色還不夠,須要找一個地板磚紋理圖。須要注意的是,紋理圖的尺寸都須要是寬和高都是2的冪,例如128x12八、256*256等,這樣出來效果纔會好。這也是3D軟件通常所要求的。另外紋理要能連續拼接不露破綻,這樣纔好。例以下面我google出來的圖:
在style裏面添加:
'top.m.texture.image': 'images/floor.png', 'top.m.texture.repeat': new mono.Vec2(10,10),
效果以下:
有圖片材質紋理,效果果真好多了。忽然想到客戶說,他們機房底面有一個方便運送設備的斜坡,必需要畫出來。這……(╯-_-)╯
後來想到twaver裏面的對象能夠支持運算,好比能夠定義一個斜的立方體,讓地板剪掉立方體,就能夠作到。因而繼續定義json:
{ name: '地板切坡', type: 'cube', width: 200, height: 20, depth: 260, translate: [-348,0,530], rotate: [Math.PI/180*3, 0, 0], op: '-', style: { …, } }
這裏定義的一個傾斜的立方體,經過translate
定義位置,rotate
定義旋轉角度,而後再經過op
定義運算符,這裏是「減去」,就用「-」表示。被剪掉的立方體也能夠設置材質、紋理、貼圖、顏色…等等,和地板同樣。看看效果:
第一步總算是有驚無險地搞定了。
下一步找了個簡單的對象,按要求走廊要放一個接待桌。爲了簡單,我決定就偷懶作一個立方體表示。
{ name: '走廊板凳', type: 'cube', width: 300, height: 50, depth: 100, translate: [350, 0, -500], }
效果以下:
這裏偷懶實際上是有緣由的。在3D裏,最重視的就是效率,千萬不要放一些很複雜的模型,尤爲是這類非業務對象。就像這個桌子,儘管只是個簡單的立方體,但只要和總體風格協調一致,再增長一點配色並啓動陰影效果後,看着就好多了:
牆體是機房裏很重要的一個部分,有好的光照、陰影的效果才能看起來更加逼真。因爲牆體是不規則的路徑,一段一段去生成還真挺麻煩的,還好引擎支持這種物體,甚至曲線路徑均可以。這裏只要在json裏面定義一組數字的座標,讓這些數字依次鏈接,組成一個牆體,最後生成3D對象放入場景中就行啦。
json定義以下:
{ name: '主牆體', type: 'path', width: 20, height: 200, translate: [-500, 0, -500], data:[ [0, 0], [1000, 0], [1000, 500], [500, 500], [500, 1000], [0, 1000], [0,0], ], }
注意這裏的類型變成了path
,data
中定義了一個二維座標數組來描述牆體。因爲牆都是從底面開始的,因此只定義它的平面的x、y座標就好了。看看效果:
不過如前文所說,仍是須要上色、上陰影,纔能有更好的效果。這裏咱們啓用陰影並諮詢設計師美眉幾個顏色值,加上去後再看下效果:
以及一些細節:
看着雪白的牆,是否是以爲少了點什麼?對,就是門。在3D機房的監控系統裏,門禁是很重要的一塊,客戶要求門應該與實際位置相對應,而且要有開門關門的動畫效果。這樣,實際的門禁信息採集上來後,就能在界面實時看到門的狀態了。
這裏,考慮到門若是直接放上去,會被牆蓋住;若是比牆厚,又難看不符合實際。仍是應該先定義一個門洞立方體,把門所在的位置挖掉:
{ name: '門洞', type: 'cube', width: 195, height: 170, depth: 30, op: '-', translate:[-350,2,500], }
恰好挖在斜坡的位置,這樣設備進屋就方便了:
不過這門沒有一個門框,感受不太生動。多一個門框會感受立體感強一些。門框能夠是一個比門洞略大的立方體,在挖門洞以前添加:
{ name: '門框', type: 'cube', width: 205, height: 180, depth: 26, translate: [-350, 0, 500], op: '+', }
加上陰影和光線等綜合效果後,還不錯,挺有檔次的。
來張全景圖看看:
接着,只要把門安上去就好了。門的定義比較簡單,就是一個薄的立方體。不過爲了作到玻璃效果,須要設置透明度,讓它看上去更像一個玻璃,再讓設計師美眉弄一張好看一點的門的圖,貼上去。儘管有了webGL,複雜的建模工做能夠省略了,不過設計師美眉的配合仍然很重要。
先作左邊的門:
{ name: '左門', type: 'cube', width: 93, height: 165, depth: 2, translate:[-397,4,500], style:{ 'm.transparent': true, 'm.texture.image': 'images/door_left.png', }
上面增長的style主要透明和貼圖兩項。看看效果:
一樣的方法,再把右側門貼上就搞定了。爲了增長體驗,也是用戶的要求,門上面設置了動畫:雙擊能夠自動打開,再雙擊能夠直接關閉。動畫功能引擎作好了封裝,在json中直接指定動畫類型就好了。不過要注意左右門的動畫旋轉方向要相反,要否則一個向裏開一個向外開感受比較怪異。
項目中,窗自己不須要有任何業務屬性,可是美觀度的要求可一點都不能少。方法和門相似,先放窗框後挖窗體。不過爲了有點變化,這裏不作窗框了,作一個窗臺,方法和道理與門相同。
{ name: '主窗戶洞', type: 'cube', width: 420, height: 150, depth: 50, translate: [200, 30, 500], op: '-', },{ name: '主窗戶臺', type: 'cube', width: 420, height: 10, depth: 40, translate: [200, 30, 510], op: '+', }
定義了一個窗洞(挖掉)、一個窗臺(添加)。一個大窗戶就作好了:
再添加一個略帶顏色的透明玻璃。玻璃設置點高光和反射,增長「玻璃」感受:
{ name: '主窗戶玻璃', type: 'cube', width: 420, height: 150, depth: 2, translate: [200, 30, 500], op: '+', style: { 'm.transparent': true, 'm.opacity':0.4, 'm.color':'#58ACFA', }, }
json中玻璃設置了透明度和顏色。這樣一個半透明的茶色玻璃就行了:
到這裏忽然在想:蓋房子若是像寫程序同樣簡單就行了,全部的程序猿就不會是無房一族單身狗了。固然寫程序和蓋房子同樣:該封裝好的要封裝好,最後就是搭積木組裝就好了。若是蓋房子都是從挖土活泥巴開始,那就杯具了。寫程序也是同樣,若是從webGL的main開始寫……這3D機房的系統要幾個月甚至幾年才能作出來呢?
按照項目實際要求,繼續增長更多建築物牆體。主要是房間外側有兩道走廊隔牆。這是一箇中間有大片透明玻璃的走廊隔牆,須要作的好看一點。因爲是直線牆,沒有複雜走向,直接用立方體定義:
{ name: '左外牆', type: 'cube', width: 20, height: 200, depth: 1300, translate: [-790, 0, 0], op: '+', }
效果以下:
再繼續挖掉中間的窗戶部分:
{ name: '左外牆洞', type: 'cube', width: 30, height: 110, depth: 1300, translate: [-790, 60, 0], op: '-', }
空白顯得很奇怪,加上玻璃試試:
{ name: '左外牆玻璃', type: 'cube', width: 4, height: 110, depth: 1300, translate: [-790, 60, 0], op: '+', style: { 'm.transparent': true, 'm.opacity':0.6, }, }
有了半透明和高光的效果,看起來就有質感了,右邊也如法炮製:
這樣,整個建築的外觀就基本完成了。最後,放一些綠植,增長些生氣吧。
作一盆植物,須要有一個空的花盆,花盆裏面有泥土,上面有一株植物。這些東西用3D作起來都有點囉嗦。不過也不難。花盆用一個大圓柱剪掉中間的小圓柱,作成空心花盆;植物用貼圖+透明模擬一下就行,不用真的去作植物的3D模型,不然要累死了。
根據上面的思路,在項目中經過仔細調整,把建立花盆的代碼封裝好,而後在json中定義花盆位置就好了。下面定義一個:
{ name: '花1', type: 'plant', translate: [560, 0, 400], }
程序中解析若是type是plant則建立植物對象並添加場景。
在房間、走廊、甚至窗臺上均可以放幾盆,窗臺上的能夠經過設置scale縮小一些,並提高其高度到窗臺位置便可。
看看下總體效果,還不賴吧。
寫了那麼一大篇,才終於把3D機房的外觀裝修完成,咱也算是個設計師程序員的混合型人才了呢。其實機房最核心的資源——機櫃,還沒找落呢,沒辦法,形象工程也是項目建設的一大亮點。
機櫃,以及其中的服務器設備。這纔是3D機房裏面最終要管理的內容。在咱們的實際項目中,這些資產都是在數據庫中存儲,並經過json接口加載到瀏覽器中顯示。這裏爲了演示方便,直接寫幾個機櫃的片斷,看一下顯示效果。
機櫃對象在項目中是這樣封裝的:用一個立方體來表示機櫃,並加上貼圖。項目中,爲了提升顯示速度,機櫃一開始並不加載內部服務器內容,而是隻顯示自身一個立方體。當用戶雙擊後,會觸發一個延遲加載器,從服務器端加載機櫃內部服務器,並加載到對應的位置上。此時,機櫃會被挖空成一個空心的立方體,以便視覺上更像一個機櫃。
定義機櫃的json以下:
{ name: '機櫃', type: 'rack', lazy: true, width: 70, depth: 100, height: 220, translate: [-370, 0, -250], severity: CRITICAL, }
上面的機櫃定義中,有一個lazy
標記,標記它是否延遲加載其內容。若是延遲加載,則雙擊觸發,不然程序顯示時直接加載其內容。Severity
是定義了機櫃的告警信息,它是否有業務告警。若是有告警,會用一個氣泡顯示在機櫃的上方,同時機櫃也會被染色成告警對應的顏色。
加入更多的機櫃看看效果:
簡單起見,這裏管理的設備假設都是機架設備,尺寸規格比較規整,所以比較容易在機櫃中組織。一個設備的外觀肯定後,在數據庫中定義好模板,加載時根據其所在機櫃的位置放置便可。
這裏只是隨機生成了幾個服務器設備,並按位置擺放。在實際應用中,能夠經過手工錄入或者智能機架報送的信息來肯定服務器的類型和位置。
若是須要監控到端口級別,還能夠在服務器彈出後,再進一步延遲加載設備商的板卡、端口對象,並點擊後進一步進行配置、監控等操做。固然加載的數據越細,對3D引擎和瀏覽器的壓力會越大。能夠經過動態延遲加載/卸載策略,獲取一些平衡折中。
純屬無聊,再作一個電視機掛在牆上。依舊,定義一個立方體、挖空屏幕,放上透明玻璃,再貼上咱們喜歡的電視節目畫面,就ok了。
{ name: '電視機體', type: 'cube', width: 150, height: 80, depth: 5, translate: [80, 100, 13], op: '+', },{ name: '電視機挖空', type: 'cube', width: 130, height: 75, depth: 5, translate: [80, 102.5, 17], op: '-', },{ name: '電視機屏幕', type: 'cube', width: 130, height: 75, depth: 1, translate: [80, 102.5, 14.6], op: '+', style: { 'front.m.texture.image': 'images/screen.jpg', }, }
固然,實際項目中,能夠換上監控大屏幕的效果:
整個場景寫到最後,我也已經腦洞大開遊刃有餘了。3D場景,尤爲是這類業務系統,並不必定要死摳模型的仿真度,才能作到「好看」的效果。先來一張全景看一下:
怎麼樣,還算精美吧?基本不輸前面看到的廣告公司的效果圖。但和效果圖一張死圖片不同,咱們這是一個能操做、能漫遊、能縮放、有動畫、顯示流暢、瀏覽器無需插件就能直接打開的3D機房小程序,就一個json文件和一百多行代碼和一天的時間就搞定了,仍是讓人有點驚訝的。
不用插件、不用3Dmax,不用模型庫,乾乾淨淨純粹的小程序,手機和平板也能用哦,並且還很流暢!上一張個人Nexus5截圖瞅瞅:
通過優化,場景加載已經控制在600毫秒之內,縮放漫遊也很流暢。固然,技術和美化永無止境,用戶的需求也變幻無窮精益求精。但只要咱們選擇好了技術和工具,就能事半功倍。Html5就是極佳的一個選擇。
Html5,也許它還不是銀彈,但它確實是很好的一個炮彈。本文這一彈,你還喜歡嗎?歡迎來信留言索取代碼、技術交流:tw-service@servasoft.com