寫在前面
權力遊戲最終季正在熱播,本人為了追劇果斷買了騰訊會員,跳廣告。不過說真的權遊的架空世界確實很令人著迷,廣袤寒冷的北境,溫暖富饒的多恩,面朝黑水灣的君臨城。巧了麼,前段時間正好看到一個 github專案 是權遊的地理要素和 mbtile資料,是廣大愛好者和官方合力貢獻且維護的。作為熱心觀眾,必須得添磚加瓦啊。
所以才有了下面的<權力的遊戲3d地圖導覽>,線上把玩地址:
其實在幾年前,我就藉助Threejs 復現過谷歌一款精美的中土地圖,那個app做得太精緻了,開場動效、音效,都很“中土”,原汁原味的托爾金味道。其實技術也很簡單,就是一個bufferPlane + texture圖 + 高程圖,根據高程圖去修改bufferPlane 對應頂點的 z 值。線上地址
概述技術過程
類似於上一篇 mapbox extrude 文章中描述的類似,我們用到的資料就一張地表影像和一張高程圖(權遊的這個高程圖是我自己先根據影像圖波段運算之後ps 修正過的,具體過程有點意思)
首先我們利用 Threejs 建立一個和影像圖寬高一致的bufferPlaneGeometry,然後拿到這個bufferPlane 的所有頂點,這時候我們要通過一個canvas去讀取高程圖中對應畫素的高度,從紅波段讀取高度,set 給bufferPlane 頂點的position.z,這就可以把平面設定為高低起伏的地形了(如下圖)
// geometry is bufferPlaneGeometry in THREEJS
// position flatArray [x,y,z,x1,y1,z1...] in geometry
var flatArray = geometry.attributes.position.array;
var verticesCount = flatArray.length / 3.0;
console.warn('bufferGeom Vertices Array length: '+ verticesCount);
for ( var i = 0, j = 0; i < verticesCount; i ++, j += 3 ) {
if (data[i] === undefined) {
console.warn(`data[${i}] is undefined..`);
break;
} else {
// set each vertice z-depth value with height
flatArray[ j-1 ] = data[i] * extrusionRatio;
}
}
複製程式碼
與 mapbox 整合
為了給三維地形加入文字標註以及興趣點 icon 等要素,我們直接把這個Threejs 圖層整合為 mapbox 的customlayer。customlayer是 mapbox 開放給webgl 開發者的一個重要介面,可以在原有的圖層列表中插入customlayer。 構造customlayer最重要的api就倆,可以參考官方文件
- onAdd(map, gl),初始化 webgl
- render(gl, matrix), 每一幀都會call 這個render函式,可以在這裡注入需要在 webgl 上下文中渲染的操作
// configuration of the custom layer for a 3D model per the CustomLayerInterface
var customLayer = {
id: '3d-terrain',
type: 'custom', // 指定是自定義圖層,不然就是 fill,symbol 等圖層.
renderingMode: '3d',
onAdd: function (map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
this.map = map;
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl // 用mapbox 的webgl作為threejs 的上下文.
});
// 把Threejs 的scene,camera以及renderer 傳入自定義的terrainLoader中,以便add(bufferPlaneMesh)
this.terrainLoader = new TerrainLoader({
scene: this.scene,
camera: this.camera,
renderer: this.renderer
});
},
render: function (gl, matrix) {
// ..省略部分 以下是將mapbox的matrix 引數同步給threejs 例項
// sync mapbox matrix with THREE camera Matrix.
var m = new THREE.Matrix4().fromArray(matrix);
var l = new THREE.Matrix4().makeTranslation(modelTransform.translateX, modelTransform.translateY, modelTransform.translateZ)
.scale(new THREE.Vector3(modelTransform.scale, -modelTransform.scale, modelTransform.scale))
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
// sync mapbox matrix with THREE camera. 更新threejs camera的投影矩陣,重新渲染,再強制觸發下mapbox 的repaint,這樣動畫就可以繼續進行了
this.camera.projectionMatrix.elements = matrix;
this.camera.projectionMatrix = m.multiply(l);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
// 把customlayer 加入label 之下,這樣文字標註就可以浮在地形圖層之上
map.on('style.load', function () {
map.addLayer(customLayer, 'roads labels');
});
複製程式碼
github專案地址 後續有空的話,會加上權力遊戲部分文件和故事線動畫,這個比較有趣一點。歡迎繼續完善3d地形的範疇,一定得會photoshop...
最近更新
- 根據GOT wiki,在介面左上角增加了故事線皮膚
- 增加了事件點的動畫效果
- 各位想加故事線的同學,只要clone下來並增補timelines.js 就可以自動增加故事點。
var timelines = [{
"id": "1",
"title": "White Walkers Emerge",
"description": "已經數千年未見蹤跡的異鬼在北境出現, 他們攻擊了守夜人軍團的一隊遊騎兵以及野人",
"location": 'Castle Black',
"camera": {
center: [18.853961295738596, 34.89038102283956],
zoom: 5.21,
pitch: 41,
bearing: 0
}
}
// ...
];
複製程式碼