基於 HTML5 WebGL 智慧城市的模擬執行
前言
智慧城市是一個系統。也稱為網路城市、數字化城市、資訊城市。
智慧城市建設是一個系統工程:首先實現的是城市管理智慧化,由智慧城市管理系統輔助管理城市,通過管理系統人們可以監視城市的執行,瞭解城市每天中發生的變化,以及及時的根據這些變化做出相應的管理;其次是包括智慧交通、智慧電力、智慧安全等基礎設施的智慧化,交通是一個城市的驅動,交通的暢通加速了城市的發展,通過 Web 視覺化的交通管理,可以更及時的瞭解交通情況,做出處理;智慧城市也包括智慧醫療、智慧家庭、智慧教育等社會智慧化和智慧企業、智慧銀行、智慧商店的生產智慧化,從而全面提升城市生產、管理、執行的現代化水平。
本 demo 使用 HT for Web 產品輕量化 HTML5/WebGL 建模的方案,構建了城市建築群場景,新增了城市道路,實現了智慧城市 Web 視覺化,還通過動畫模擬了城市的執行。
demo 地址:http://www.hightopo.com/demo/intelligent-city/entry/dest/index.html
預覽圖:
程式碼實現
載入場景
首先新建一個場景,並將場景新增到頁面中。
let dm = this.dm = new ht.DataModel();
let entryG3d = this.entryG3d = new ht.graph3d.Graph3dView(dm);
entryG3d.addToDOM(); // 將場景新增到頁面中
HT 的元件一般都會嵌入 BorderPane、SplitView 和 TabView 等容器中使用,而最外層的 HT 元件則需要使用者手工將 getView() 返回的底層 div 元素新增到頁面的 DOM 元素中,這裡需要注意的是,當父容器大小變化時,如果父容器是 BorderPane 和 SplitView 等這些 HT 預定義的容器元件,則 HT 的容器會自動遞迴呼叫孩子元件 invalidate 函式通知更新。但如果父容器是原生的 html 元素, 則 HT 元件無法獲知需要更新,因此最外層的 HT 元件一般需要監聽 window的視窗大小變化事件,呼叫最外層元件 invalidate 函式進行更新。
為了最外層元件載入填充滿視窗的方便性,HT 的所有元件都有 addToDOM 函式,其實現邏輯如下,其中 iv 是 invalidate 的簡寫:
addToDOM = function(){
var self = this,
view = self.getView(), // 獲取元件 div
style = view.style;
document.body.appendChild(view); // 將元件新增到文件物件中
style.left = '0';
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize', function () { self.iv(); }, false); // 監聽視窗變化,重新整理元件
}
接下來反序列化城市場景 json。
ht.Default.xhrLoad('scenes/園區/城市demo.json', (text) => {
let json = ht.Default.parse(text); // 還原 json 字串
let scene = json.scene; // 獲取 json 中設定的投影引數
entryG3d.setEye(scene.eye); // 設定視角
entryG3d.setCenter(scene.center); // 設定目標中心點
entryG3d.setFar(scene.far); // 設定遠端截面位置
entryG3d.setNear(scene.near); // 設定近端截面位置
dm.deserialize(text); // 場景反序列化
this.setSkyBox(entryG3d); // 設定天空球
this.initentryG3dEvent(); // 新增場景監聽事件
this.startAnimate(); // 啟動城市動畫
this.startCarAnimate(['car1Line'], dm.getDataByTag('car1')); // 啟動消防小車1動畫
this.startCarAnimate(['car2Line'], dm.getDataByTag('car2')); // 啟動消防小車2動畫
});
場景渲染
1. 環境光貼圖:將貼圖影像渲染在場景中,通過 node.s(‘envmap’, 0.5) 來設定節點的渲染程度,主要用於地板新增物體倒影效果,也可像本 demo 一樣新增星光點綴。
dataModel.setEnvmap('環境光貼圖.png'); // 貼圖要求寬高畫素為 2^n
node.s('envmap', 0.1);
對比圖:
右圖中環境光貼圖為星光,城市中心區域有了藍色的星空色,在 demo 中旋轉場景也能看到明顯的星光變化。
2. 輝光:本是指低壓氣體中氣體放電的物理現象,用在 3D 場景中將亮色突出顯示,呈現一種發光的效果,可控制輝光強度,輝光顯示的範圍和發光閥值。
g3d.enablePostProcessing('Bloom', true); // 開啟輝光
module = g3d.getPostProcessingModule('Bloom');
module.strength = 0.18; // 強度
module.threshold = 0.62; // 閾值
module.radius = 0.4; //範圍
g3d.iv(); // 重新整理拓撲
對比圖:
3. 景深:對場景中心周圍的清晰程度的控制,將周圍虛化,美化畫面,突出主體,增強透視,可控制景深閥值(周圍模糊範圍程度)。
g3d.enablePostProcessing('Dof', true); // 開啟景深
module = g3d.getPostProcessingModule('Dof');
module.aperture = 0.18; // 景深閥值
module.image= '景深貼圖.png'; // 景深貼圖
g3d.iv(); // 重新整理拓撲
對比圖:
可以看到右圖中衛星區域和最左側變得模糊。
4. 天空球:將場景模型放置在一個大的球體中,球體內部進行貼圖,來模擬天空。
node = new ht.Node()
node.s({
'shape3d':'sphere', // 球體
'shape3d.image': 'earth' // 貼圖路徑
});
node.s3(10000, 10000, 10000);
g3d.dm().add(node);
g3d.setSkyBox(node); // 設定天空球
動畫實現
載入後的城市場景如下圖所示:
我們可以看到建築物群有各自的資料展示皮膚,圍繞著中心大樓有一個光圈列車模型,是模擬的城市中心列車,還有右邊一個城市衛星,現在我們來讓資料皮膚資料變化,讓列車、衛星開始動起來。
動畫的實現是用排程函式來實現的(排程手冊),我們先了解一下排程函式的用法:
HT 中排程進行的流程是,先通過 dataModel 新增排程任務,dataModel 會在排程任務指定的時間間隔到達時, 遍歷 dataModel 所有圖元回撥排程任務的 action 函式,可在該函式中對傳入的 data 圖元做相應的屬性修改以達到動畫效果。
dataModel.addScheduleTask(task) 新增排程任務,其中 task 為 json 物件,可指定如下屬性:
- interval:間隔毫秒數,預設值為10
- enabled:是否啟用開關,預設為 true
- action:間隔動作函式,該函式必須設定
dataModel.removeScheduleTask(task) 刪除排程任務,其中 task 為以前新增過的排程任務物件。
動畫實現程式碼如下:
startAnimate() {
let dr = Math.PI / 180 * 2, PI2 = Math.PI * 2;
let dm = this.dm;
let rotateOval = dm.getDataByTag('rotateOval'); // 獲取城市列車模型
let logo = dm.getDataByTag('logo'); // 獲取城市衛星模型
// 設定動畫引數
let roatateTask = this.roatateTask = {
interval: 100,
action: function(data){
if(data === rotateOval || data === logo){
data.setRotation((data.getRotation() - dr) % PI2);
}
}
};
dm.addScheduleTask(roatateTask); // 將動畫加入到排程任務中
// 獲取建築物資料皮膚
let dglsd = dm.getDataByTag('dglsd'); // 戴谷嶺隧道
let xlsd = dm.getDataByTag('xlsd'); // 杏林隧道
let xmg = dm.getDataByTag('xmg'); // 廈門港
let jcglzx = dm.getDataByTag('jcglzx'); // 機場管理中心
// 模擬建築物資料皮膚的動態展示
let valueChangeTask = this.valueChangeTask = {
interval: 1000,
action: function(data){
if(data === dglsd){
data.a('carHour', util.randomNumBetween(3000, 6000));
}
if(data === xlsd){
data.a('carHour', util.randomNumBetween(3000, 6000));
}
if(data === xmg){
data.a('ttl', util.randomNumBetween(3, 10));
data.a('zy', util.randomNumBetween(0, 100));
}
if(data === jcglzx){
data.a('sskll', util.randomNumBetween(500, 2000));
}
}
};
dm.addScheduleTask(valueChangeTask); // 將資料變化加入到排程任務中
}
實現的動畫效果如下圖:
demo 還模擬了消防車趕往火災發生地,動畫如下:
消防車的行駛用到了 ht.Default.startAnim,我們先來了解一下:
ht.Default.startAnim({
frames: 12, // 動畫幀數
interval: 10, // 動畫幀間隔毫秒數
easing: function(t){ return t * t; }, // 動畫緩動函式,預設採用 ht.Default.animEasing
finishFunc: function(){ console.log('Done!') }, // 動畫結束後呼叫的函式。
action: function(v, t){ // action函式必須提供,實現動畫過程中的屬性變化。
node.setPosition( // 此例子展示將節點`node`從位置`p1`動畫到位置`p2`。
p1.x + (p2.x - p1.x) * v,
p1.y + (p2.y - p1.y) * v
);
}
});
以上為 Frame-Based 方式動畫, 這種方式使用者通過指定 frames 動畫幀數,以及 interval 動畫幀間隔引數控制動畫效果。
ht.Default.startAnim({
duration: 500, // 動畫週期毫秒數,預設採用`ht.Default.animDuration`
action: function(v, t){
...
}
});
以上為 Time-Based 方式動畫,該方式使用者只需要指定 duration 的動畫週期的毫秒數即可,HT 將在指定的時間週期內完成動畫。
由於 js 語言無法精確控制 interval 時間間隔, 採用 Frame-Based 不能精確控制動畫時間週期,即使相同的 frames 和 interval 引數在不同的環境,可能會出現動畫週期差異較大的問題, 因此 HT 預設採用 Time-based 的方式,如果不設定 duration 和 frames 引數,則 duration 引數將被系統自動設定為 ht.Default.animDuration 值。
消防車行駛實現程式碼如下:
startCarAnimate(polylineTagArr, airNode) { // 傳入消防車行駛路線 tag(唯一標籤)組和消防車模型節點
let dm = this.dm;
let entryG3d = this.entryG3d;
let curIndex = 0;
let polyline = dm.getDataByTag(polylineTagArr[curIndex]); // 獲取消防路線 ht.PolyLine(http://www.hightopo.com/guide/guide/core/shape/ht-shape-guide.html#ref_different)
let prePos = airNode.p3(); // 獲取節點初始位置
let preRotate = airNode.r3(); // 獲取節點初始旋轉角度
let lineLength = entryG3d.getLineLength(polyline); // 獲取管道長度
let params = { // 設定消防車行駛動畫引數
duration: 10000,
easing: function(t){
return t * t;
},
action: function(v, t){
let offset = entryG3d.getLineOffset(polyline, lineLength * v), // 獲取管道指定比例的偏移資訊
point = offset.point,
px = point.x,
py = point.y,
pz = point.z,
tangent = offset.tangent,
tx = tangent.x,
ty = tangent.y,
tz = tangent.z;
airNode.p3(px, py, pz); // 移動消防車到下一位置
airNode.lookAt([px + tx, py + ty, pz + tz], 'front'); // 消防車沿著管道方向轉向
},
finishFunc: function(){
if(curIndex < polylineTagArr.length - 1) { // 進入下一段路線
curIndex++;
}
else {
curIndex = 0; // 返回第一段路線
}
if(curIndex === 0) { // 消防車返回原點
airNode.p3(prePos);
airNode.r3(preRotate);
}
polyline = dm.getDataByTag(polylineTagArr[curIndex]);
lineLength = entryG3d.getLineLength(polyline);
this.carAni = ht.Default.startAnim(params); // 執行下一段行駛動畫
}
};
this.carAni = ht.Default.startAnim(params);
}
消防車的行駛可總結為先獲取行駛道路的管線長度資訊,計算一定比例的管線偏移點,移動消防車位置到計算的偏移點,調整消防車模型的朝向,計算下一偏移點進行位移,直到到達當前管線尾,然後獲取下一管線的長度資訊,繼續進行偏移。
程式碼中提到的三個方法:getLineLength、getLineOffset、lookAt,我們來了解一下。
1. g3d.getLineLength(edgeOrPolyLine)
根據引數 edgeOrPolyLine 方法命名我們就可以知道此方法是獲取連線或管道的長度。
2. g3d.getLineOffset(edgeOrPolyLine, offset)
- edgeOrPolyLine 連線或者管道
- offset 偏移百分比
此方法獲取連線或者管道的偏移資訊,返回 { point, tangent },其中 point 是 3D 座標,tangent 是 point 點沿當前線的切線向量。
3. node.lookAt(point, direction)
- point 3D 座標點
- direction 節點某一面,值可為 ‘front | back | left | right | top | bottom’
將節點的某一面朝向空間某座標點。
消防車行駛轉向示意圖:
1. 起始位置;
2. 通過 setPosition3d(point.x, point.y, point.z) 到達下一位置,紅線為 tangent 切線向量;
3. 通過 node.lookAt([point.x + tangent.x, point.y + tangent.y, point.z + tangent.z], ‘front’); 將車頭擺向切線方向;
4.5. 重複以上步驟。
其中 [point.x + tangent.x, point.y + tangent.y, point.z + tangent.z] 即為目標點 target 的座標。
總結
智慧城市系統還包括智慧樓宇的管理:
http://www.hightopo.com/demo/ht-smart-building/
地鐵是一個城市的核心交通工具,地鐵站的管理也必不可少:
http://www.hightopo.com/demo/ht-subway/
相關文章
- 基於 HTML5 WebGL 的智慧城市(一)HTMLWeb
- 基於 HTML5 WebGL 構建智慧城市 3D 場景HTMLWeb3D
- 基於 HTML5 WebGL 構建智慧數字化城市 3D 全景HTMLWeb3D
- 基於 HTML5 WebGL 的 智慧樓宇能源監控系統HTMLWeb
- 基於 WebGL 的 HTML5 3D 智慧樓宇監控系統WebHTML3D
- 基於 HTML5 的 WebGL 3D 智慧樓宇監控系統HTMLWeb3D
- 基於 HTML5 WebGL 的垃圾分類系統HTMLWeb
- 基於 HTML5 WebGL 的 3D 挖掘機HTMLWeb3D
- 基於WebGL HTML5 的場景小遊戲WebHTML遊戲
- 基於 HTML5 WebGL 的 3D 機房HTMLWeb3D
- 基於 HTML5 WebGL 的 3D 科幻風機HTMLWeb3D
- 基於 HTML5 的 WebGL 3D 隧道監控HTMLWeb3D
- 基於 HTML5 WebGL 與 GIS 的智慧機場大資料視覺化分析HTMLWeb大資料視覺化
- 基於 WebGL HTML5 的 3D 模型分離控制WebHTML3D模型
- 基於 HTML5 WebGL 的 3D 工控裙房系統HTMLWeb3D
- 基於 HTML5 WebGL 的 3D 模型斜面生成HTMLWeb3D模型
- 基於 WebGL 的 HTML5 3D 工控裙房系統WebHTML3D
- 基於 HTML5 WebGL 的 3D 網路拓撲圖HTMLWeb3D
- 基於 HTML5 + WebGL 的 3D 風力發電場HTMLWeb3D
- 基於 WebGL 的 HTML5 3D SCADA 主站系統WebHTML3D
- 基於 WebGL 的 HTML5 3D 網路拓撲圖WebHTML3D
- 基於 WebGl HTML5 的 3D 視覺化機房WebHTML3D視覺化
- 於 HTML5 WebGL 的民航客機飛行監控系統HTMLWeb
- 關於虛擬模擬教學系統使用WebGL部署的痛點分析Web
- 基於 HTML5 + WebGL 的太陽系 3D 展示系統HTMLWeb3D
- 基於 WebGL 實現的 HTML5 3D “彈力”佈局WebHTML3D
- 基於 HTML5 WebGL 實現的 3D “彈力”佈局HTMLWeb3D
- 基於 WebGL 的 HTML5 3D 棉花加工監控系統WebHTML3D
- 基於 HTML5 WebGL 的 3D 棉花加工監控系統HTMLWeb3D
- 基於 HTML5 WebGL 的 3D 場景中的燈光效果HTMLWeb3D
- 基於HTML5 WebGL的工業化3D電子圍欄HTMLWeb3D
- 基於 HTML5 WebGL 的 3D 風機視覺化系統HTMLWeb3D視覺化
- 基於 WebGL 的 HTML5 網路拓撲結構 3D 圖WebHTML3D
- 基於 WebGL 的 HTML5 3D 工控隧道視覺化系統WebHTML3D視覺化
- 基於 HTML5 WebGL 的挖掘機 3D 視覺化應用HTMLWeb3D視覺化
- 基於 WebGL 的 HTML5 低碳工業園區監控系統WebHTML
- 基於 HTML5 WebGL 的低碳工業園區監控系統HTMLWeb
- 基於 HTML5 WebGL 的加油站 3D 視覺化監控HTMLWeb3D視覺化