美文网首页GIS - Mapbox数据可视化研究所AR/视觉兴趣小组
《权力的游戏》3d地图-基于Mapbox customlayer

《权力的游戏》3d地图-基于Mapbox customlayer

作者: 四爷在此 | 来源:发表于2019-05-02 17:17 被阅读6次

    写在前面

    最终季!!转自豆瓣,如侵权请联系... https://movie.douban.com/photos/photo/2549876231/

    权力游戏最终季正在热播,本人为了追剧果断买了腾讯会员,跳广告。不过说真的权游的架空世界确实很令人着迷,广袤寒冷的北境,温暖富饶的多恩,面朝黑水湾的君临城。巧了么,前段时间正好看到一个 github项目 是权游的地理要素和 mbtile数据,是广大爱好者和官方合力贡献且维护的。作为热心观众,必须得添砖加瓦啊。

    所以才有了下面的 3d 地形图层,在线把玩地址

    image.png

    其实在几年前,我就借助Threejs 复现过谷歌一款精美的中土地图,那个app做得太精致了,开场动效、音效,都很“中土”,原汁原味的托尔金味道。其实技术也很简单,就是一个bufferPlane + texture图 + 高程图,根据高程图去修改bufferPlane 对应顶点的 z 值。在线地址

    概述技术过程

    类似于上一篇 mapbox extrude 文章中描述的类似,我们用到的数据就一张地表影像和一张高程图(权游的这个高程图是我自己ps 的,具体过程有点意思)


    左边高程图+右边影像图

    首先我们利用 Threejs 建立一个和影像图宽高一致的bufferPlaneGeometry,然后拿到这个bufferPlane 的所有顶点,这时候我们要通过一个canvas去读取高程图中对应像素的高度,从红波段读取高度,set 给bufferPlane 顶点的position.z,这就可以把平面设置为高低起伏的地形了(如下图)

    根据高程图设置bufferPlane 的顶点高度
    // 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...

    相关文章

      网友评论

        本文标题:《权力的游戏》3d地图-基于Mapbox customlayer

        本文链接:https://www.haomeiwen.com/subject/sdfupttx.html