記得15年那會兒,我給人講WebGL,還得從頭科普,三令五申要用Chrome,末了再強調一句,記得啓用WebGL功能。今天,但凡懂點兒Web開發的,都會來一句「網頁3D用WebGL」。數據庫
怎麼形容這種感覺呢?canvas
這兩年的技術發展,你們想必都看在眼裏,單用「爆發」二字,實在難以描述其中的驚天鉅變。而回到網頁3D這個話題上,我想,最大的驅動力,莫過於16年至今虛擬現實的迅速崛起,完全推動了三維可視化技術的日新月異,而物聯網發力,又開啓了一扇通往新世界的大門。後端
遊戲界至今爭論不休的Unity仍是HTML5,依我看至少WebGL活的好好的,而插件技術麼,則讓我想起了一首悲傷的歌:dying in the sun…數組
今天固然仍是給你們介紹一款最新的三維可視化成果,開始正題前,我想先盤點一下這兩年還算靠譜的一些三維應用。架構
工業上的,偏銷售側有複雜零件的三維展現,依託零件原有模型,作好參數轉換,比較容易實現。偏管理側,主要是廠房車間和生產設備的三維監控;電力行業,有無人值守變電站的巡檢和監控,結合三維能夠進行遠程巡檢做業,下降人工做業的風險;倉庫和糧倉,引入三維後,能夠結合庫房系統、環境系統,進行全方位的管理;礦山和隧道,這個很好理解:做業越是危險、對環境要求越高的地方,越是須要虛擬仿真;3D家裝設計, 一般是在線模式,拖拽設計,結合傢俱販售,早先是以Stage3D爲主,這兩年也看到不少WebGL的案例;博物館、圖書館、檔案館的導覽結合解說,複雜設備的虛擬仿真培訓,商品展現,這個比較多了,像虛擬試衣間,商品的三維在線瀏覽,好比剛看到這款霸氣側漏的零食:框架
開個玩笑。其實無論哪一個行業,三維應用很大的一部分工做量源於建模,而此次咱們想分享的是和上述應用徹底不一樣的一個案例。函數
此次的案例,對模型幾乎沒有要求,只是用了最基本的形狀元素,可是卻能夠解決企業在不斷髮展壯大,尤爲是信息化進程不斷深刻的過程當中,都會遇到的問題,那就是如何將複雜流程可視化。優化
流程,看起來很簡答的兩個字,英文process。咱們這裏所說的流程,主要是企業的業務流程,如生產流程、各種行政申請流程、財務審批流程、人事處理流程、質量控制及客服流程等等。動畫
業務流程對於企業的意義不只僅在於對企業關鍵業務的一種描述;更在於對企業的業務運營有着指導意義,這種意義體如今對資源的優化、對企業組織機構的優化以及對管理制度的一系列改變。這種優化的目的實際也是企業所追求的目標:下降企業的運營成本,提升對市場需求的響應速度,爭取企業利潤的最大化。(from智庫百科MBAlib)this
爲啥我忽然開始拽商業名詞了呢?由於,這是甲方爸爸給出的一個難題。
甲方爸爸的企業,部門繁多,流程複雜,爲了提高業務流程的效率,同時優化企業自身的管理,提出了一個將其現有業務流程進行三維可視化的需求。
爲何要把流程進行三維可視化呢?
通俗的說,由於平面的實在是看不清理不順了;時髦點講,只有「升維」,才能展現和包容更多的信息。
看到這裏的三體同好們,請不要吝嗇向我隔空揮舞小手!
甲方爸爸的信息實在太多,並且不能透露,咱們就找其中幾個簡單的舉個例子,好比每一個企業天天都在處理的報帳流程:(應要求馬賽克了一些文字)
報帳流程業務圖:
二層邏輯交互圖:
報帳業務基礎架構:
看到如此複雜的結構,不管你是如下哪一種表情,都不要驚慌:
咱們先簡單梳理下流程圖的大體框架,一共能夠分爲五層:
爲了讓這五層之間的關係一目瞭然,分層展現是必不可少的。每一個層次裏,又分爲幾個模塊,層次與層次、模塊與模塊之間都有業務上的聯繫。根據梳理的邏輯關係,我先整了一個初稿。流程和業務先用簡單的方塊和圓柱代替,底層的機櫃模型,直接用存貨模型,機房相關的模型,咱們大大的有。
雖然看起來層次感有了,不過這只是個總體的框架demo,咱們在這基礎上一步步修改。
說下層模型的圓弧效果。爲了把每一個層作成圓弧的效果,我把層模型拆解成了由9個簡單模型組合而成,上圖給大家看(原諒我圖畫得渣渣)。
模型一、二、三、四、5均爲厚度相同的立方體,模型六、七、八、9爲大小相等的1/4圓柱體,9個模型組合成層模型。
模型1的代碼:(模型1,2,3,4,5差很少,就只貼模型1的代碼了。)
var centerNode = new mono.Cube({ width: width, height: height, depth: depth, }); centerNode.s({ 'm.type': 'phong', 'm.color': color });
模型6的代碼:
var leftTopCylinder = new mono.Cylinder({ radiusTop: radius, radiusBottom: radius, height: height, arcLength: Math.PI / 2, //圓柱的圓弧所佔長度 arcStart: Math.PI //圓弧開始的角度 }); leftTopCylinder.s({ 'm.type': 'phong', 'm.color': color }); leftTopCylinder.p(-width / 2, 0, -depth / 2);
9個模型合併:
var combo = new mono.ComboNode([centerNode, leftNode, rightNode, topNode, bottomNode, leftTopCylinder, rightTopCylinder, leftBottomCylinder, rightBottomCylinder],['+'],true);
首先,增長了背景圖片,選取的是一張星空的圖片,以後根據背景修改了配色和部分模型。
效果仍是不錯的,看起來更加大氣。增長背景代碼:
network.setClearColor(0, 0, 0); network.setClearAlpha(0); network.setBackgroundImage('./images/background.jpg');
圖中顯示文字相似對話框的東西實際上是billboard,先建立一個billboard,再用canvas畫一張圖做爲貼圖貼到上面就能夠了。
var billboard = new mono.Billboard(); var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); context.font = "130px 微軟雅黑"; var array = []; if (text.indexOf("\n")) { array = text.split("\n"); } else { array = [text] } var length = 0; for (var i = 0; i < array.length; i++) { if (i == 0) { length = context.measureText(array[i]).width; } else { length = Math.max(context.measureText(array[i]).width, length); } } var size = mono.Utils.getMaxTextSize(array, context.font); var width = mono.Utils.nextPowerOfTwo(length); var oHeight = size.height; var arrowHeight = 40; var arrowWidth = 80; var height = mono.Utils.nextPowerOfTwo(oHeight + arrowHeight); canvas.height = height; canvas.width = width; var lineHeight =(height - arrowHeight - 40) / array.length; var oLineHeight = oHeight / array.length; var radius = width / 16; var context = canvas.getContext('2d'); context.globalAlpha = 0.9; context.fillStyle = bgColor; context.save(); context.beginPath(); context.moveTo(radius + 10, 10); context.lineTo(width - radius - 10, 10); context.arcTo(width - 10, 10, width - 10, radius + 10, radius); context.lineTo(width - 10, height - arrowHeight - radius - 10); context.arcTo(width - 10, height - arrowHeight - 10, width - radius -10, height - arrowHeight - 10, radius); context.lineTo(width / 2 + arrowWidth / 2 - 10, height - arrowHeight - 10); context.lineTo(width / 2 - 10, height - 10); context.lineTo(width / 2 - arrowWidth / 2 - 10, height - arrowHeight - 10); context.lineTo(radius + 10, height - arrowHeight - 10); context.arcTo(10, height - arrowHeight - 10, 10, height - arrowHeight - radius - 10, radius); context.lineTo(10, radius + 10); context.arcTo(10, 10, radius + 10, 10, radius); context.closePath(); context.fill(); context.globalAlpha = 1; context.lineWidth = 10; context.strokeStyle = bgColor; context.stroke(); context.restore(); context.fillStyle = fontColor; context.textBaseline = 'middle'; context.font = "120px 微軟雅黑"; for (var i = 0; i < array.length; i++) { var text = array[i]; length = context.measureText(text).width; context.fillText(text, (width - length) / 2, lineHeight * (i + 0.5)); } billboard.s({ 'm.texture.image': canvas, 'm.texture.offset': new mono.Vec2(0, 0.005), 'm.texture.anisotropy': 8, 'm.alignment': mono.BillboardAlignment.bottomCenter });
這兩種屬於obj模型,是設計小姐姐作的,而後咱們經過make.Default.register函數定義模型,經過make.Default.load函數加載使用模型。
爲了使效果更逼真,咱們給模型作了環境貼圖。
object3d.setStyle('m.envmap.image', make.Default.getEnvMap('envmap5'));
單純的靜態圖看起來有些單調,因此咱們給連線加了動畫效果:找一張一半透明一半有顏色的圖片,做爲貼圖貼在連線上,利用動畫函數使貼圖不斷平移,就實現了下面的效果。
最底層的模型採用了實體模型,真實感更強:
上面也提到過,層與層、層內各個模塊中之間存在錯綜複雜的多層嵌套關係,爲了展示這種關係,那確定就要連線,話很少說,直接上圖。
線的類型有兩種,層與層之間的連線類型是link,每層模塊之間的連線類型是pathLink,建立pathLInk代碼以下:
createPathLink: function (data) { var box = main.sceneManager.getDataBox(); var fromNode = main.sceneManager.getNodeByDataOrId(data.fromId); var toNode = main.sceneManager.getNodeByDataOrId(data.toId); var radius = data.path.radius || 3; var color = data.path.color || 'yellow'; var endCap = data.path.endCap; var startCap = data.path.startCap; var linkType = data.routeType; var flow = data.path.flow || ''; var workflowId = data.workflowId || ''; if (fromNode && toNode) { var link = new mono.PathLink(fromNode, toNode, data.id); var plength = link.getPath().getLength(); link.setRadius(radius); link.s({ 'm.type': 'phong', 'm.color': color, 'm.ambient': color }); link.workflowId = workflowId; if (endCap) { var endCapSize = data.path.endCapSize || 10; var endCapR = data.path.endCapR || 2; link.setEndCap(endCap); link.setEndCapSize(endCapSize); link.setEndCapR(endCapR); } if (startCap) { var startCapSize = data.path.startCapSize || 10; var startCapR = data.path.startCapR || 2; link.setStartCap(startCap); link.setStartCapSize(startCapSize); link.setStartCapR(startCapR); } if (linkType) { link.setLinkType(linkType); } box.add(link); } }
link類型連線與pathLink類型連線大致相同,之因此層與層之間選擇link類型,有兩個緣由:一是當鏡頭拉近時,link類型的連線粗細不會改變,二是便於控制拐點,就是下圖中的紅圈處。
link.setLinkType('control');//control屬性控制連線的拐點 link.setControls(controls);//controls爲數組
這樣就能夠呈現圖中的傘狀效果啦。
爲了增長點朦朧感以及讓傘狀效果更好,咱們特地添加了一點光環,有沒有感受金光從天而降呢?此時請想象本身45°角仰望天空,金光照在臉上。
基礎打好,下面就能夠加上動畫,執行流程了。先上圖:
點擊左邊的按鈕,出現圖中的白色小球,沿着連線運動,完整展示整個流程步驟。固然,鏡頭會隨着小球切換,這樣小球時刻在視線正中,媽媽不再用擔憂個人視線被擋住。
鏡頭切換的代碼也很簡單:
var pos = link.getPointAt(v); workflowSphere.p(pos); billboard.p(pos.clone().add(new mono.Vec3(0, 250, 0))); var camera = main.sceneManager.network3d.getCamera(); camera.lookAt(pos); camera.p(pos.clone().sub(this._cameraOffset));
最後聊聊數據。爲了方(tou)便(lan),咱們將流程圖的全部數據都存放在後臺。在後端頁面,能夠設置流程圖的結構、邏輯、流程節點的樣式等。
利用Ajax獲取模型數據,而後三行代碼即可建造一個3D流程圖系統。
dataManager.addCategoryFromJson(loadData.categories); dataManager.addDataTypeFromJson(loadData.datatypes); dataManager.addDataFromJson(loadData.datas);
一樣,能夠在後端頁面設置連線的樣式、顏色、起點、終點等等,獲取到連線數據後,利用上文提到的方法即可繪製出所須要的連線。連動畫的起點、走向一樣能夠在後端頁面設置。
若是甲方爸爸以爲某個流程有問題,須要修改時,不要怕,默默打開後端頁面改幾個節點就行了。速度這麼快,快誇我快誇我。
總而言之,只須要經過數據配置便可生成不一樣的三維流程,知足客戶的各類需求。
對demo感興趣的同窗,能夠給我發郵件:tw-service@servasoft.com,甲方爸爸的數據不能給你,demo仍是能夠給大家看一眼的。