基于WebGL的方式使用OpenLayers

1. 引言

在绘制海量数据时,使用GPU进行绘制可有效减少CPU的负载,提升绘制时的速度在浏览器中,可以使用WebGL的方式与GPU交互

OpenLayers是一个常用的GIS相关的JavaScript前端库,支持Canvas和WebGL两种方式渲染地图,默认采用的是Canvas

OpenLayers 6支持不同的图层(Layer)使用不同的渲染方式,WebGL拥有更高的性能,更适合渲染大数据

本文这里结合官方示例,描述OpenLayers的WebGL渲染的使用方法

OpenLayers官网:OpenLayers - Welcome

OpenLayers的GitHub站点:openlayers/openlayers: OpenLayers (github.com)

OpenLayers API文档:OpenLayers v6.15.1 API - Index

2. 绘制Points

绘制大量点数据是最常规的需求,OpenLayers 在6.1.x的API文档中提出了使用WebGL直接绘制点图层的API:

image-20220722005324841

然而,在之后的版本中(截至目前版本为6.15.1), ol.layer.WebGLPoints这个API没有在API文档中显示,但是API还是存在的

OpenLayers的issue中显示,这个API还在测试中,没有正式发布:

OpenLayers的官方示例中多处使用这个API:

参考这几个示例,我们使用WebGL绘制点图层的核心代码如下:

map.addLayer(new ol.layer.WebGLPoints({
    source: new ol.source.Vector({
        url: 'https://openlayers.org/en/latest/examples/data/geojson/world-cities.geojson',
        format: new ol.format.GeoJSON(),
        wrapX: true,
    }),
    style: {
        symbol: {
            symbolType: 'circle',
            size: 8,
            color: 'rgb(255, 0, 0)',
            opacity: 0.2,
        },
    }
}));

WebGLPoints图层需要矢量点数据源(Source)和绘制的样式(Style)

这里的样式区别于Canvas图层,是一个symbol对象,主要指定绘制的类型、尺寸、颜色、透明度等

更具体的参数可以参考:

绘制WebGLPoints图层的完整代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- openlayers cdn -->
    <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.15.1/css/ol.css" type="text/css">
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.15.1/build/ol.js"></script>
    <style>
        html,
        body {
            height: 100%;
        }

        body {
            margin: 0;
            padding: 0;
        }

        #map {
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        var map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM()
                })
            ],
            view: new ol.View({
                center: ol.proj.fromLonLat([-119.644, 34.5158]),
                zoom: 2
            })
        });
        // var vectorLayer = new ol.layer.Vector({
        //     source: new ol.source.Vector({
        //         url: 'https://openlayers.org/en/latest/examples/data/geojson/world-cities.geojson',
        //         format: new ol.format.GeoJSON(),
        //         wrapX: true,
        //     })
        // });
        // map.addLayer(vectorLayer);

        map.addLayer(new ol.layer.WebGLPoints({
            source: new ol.source.Vector({
                url: 'https://openlayers.org/en/latest/examples/data/geojson/world-cities.geojson',
                format: new ol.format.GeoJSON(),
                wrapX: true,
            }),
            style: {
                symbol: {
                    symbolType: 'circle',
                    size: 8,
                    color: 'rgb(255, 0, 0)',
                    opacity: 0.2,
                },
            }
        }));
    </script>


</body>

</html>

其中:

  • 数据来源是OpenLayers示例数据
  • 地图瓦片是来自OSM,使用Canvas绘制
  • 注释部分的代码是使用Canvas绘制点图层,可以感受到使用WebGL的方式更流畅

最后,渲染的结果如下:

image-20220722020335407

这个API毕竟还是在实验阶段,并且只是点图层,如果不满足需求,可以进行进一步定制重写

可以参考:

创建扩展的Layer类并使用WebGL Renderer进行重写createRenderer方法

当然,上述这个博客的方法目前并不适用,现在的WebGL Renderer必须设置好着色器代码:

image-20220722013008722

但是实现原理是基本一致,参考官方实现的ol.layer.WebGLPoints:

其Render为WebGLPointsLayerRenderer

createRenderer() {
    return new WebGLPointsLayerRenderer(this, {
        vertexShader: this.parseResult_.builder.getSymbolVertexShader(),
        fragmentShader: this.parseResult_.builder.getSymbolFragmentShader(),
        hitVertexShader:
        !this.hitDetectionDisabled_ &&
        this.parseResult_.builder.getSymbolVertexShader(true),
        hitFragmentShader:
        !this.hitDetectionDisabled_ &&
        this.parseResult_.builder.getSymbolFragmentShader(true),
        uniforms: this.parseResult_.uniforms,
        attributes: this.parseResult_.attributes,
    });
}

2. 绘制Tiles

绘制大量的瓦片也是日常所需,OpenLayers提供了使用WebGL绘制Tile的API:

image-20220722013703814

参考OpenLayers官方的几个示例:

参考这几个示例,我们使用WebGL绘制Tile图层的核心代码如下:

map.addLayer(new ol.layer.WebGLTile({
    source: new ol.source.OSM()
}));

可设置的参数主要有Style、Source、Opacity等,更多参数的设置请参考:

绘制WebGLTile图层的完整代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- openlayers cdn -->
    <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.15.1/css/ol.css" type="text/css">
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.15.1/build/ol.js"></script>
    <style>
        html,
        body {
            height: 100%;
        }

        body {
            margin: 0;
            padding: 0;
        }

        #map {
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        var map = new ol.Map({
            target: 'map',
            layers: [],
            view: new ol.View({
                center: ol.proj.fromLonLat([-119.644, 34.5158]),
                zoom: 2
            })
        });

        map.addLayer(new ol.layer.WebGLTile({
            source: new ol.source.OSM()
        }));
        // map.addLayer(new ol.layer.WebGLTile({
        //     source: new ol.source.OSM()
        // }));
        // map.addLayer(new ol.layer.WebGLTile({
        //     source: new ol.source.OSM()
        // }));
        // map.addLayer(new ol.layer.WebGLTile({
        //     source: new ol.source.OSM()
        // }));

        // map.addLayer(new ol.layer.Tile({
        //     source: new ol.source.OSM()
        // }));
        // map.addLayer(new ol.layer.Tile({
        //     source: new ol.source.OSM()
        // }));
        // map.addLayer(new ol.layer.Tile({
        //     source: new ol.source.OSM()
        // }));
        // map.addLayer(new ol.layer.Tile({
        //     source: new ol.source.OSM()
        // }));
    </script>


</body>

</html>

其中:

  • 地图瓦片来源于OSM
  • 注释部分的代码是与Canvas绘制Tile图层(四个图层)对比,可以感受到使用WebGL的方式更流畅

最后,渲染的结果如下:

image-20220722020302010

4. 参考资料

[1]OpenLayers Examples

[2]OpenLayers v6.15.1 API - Index

[3]openlayers/openlayers: OpenLayers (github.com)

[4]Openlayers指南-WebGL - 简书 (jianshu.com)

[5]Rendering points · HonKit (openlayers.org)

[6]openlayers6实现webgl点图层渲染效果(附源码下载) - 知乎 (zhihu.com)

[7]OpenLayers中切片图层TileLayer的渲染解析 - 简书 (jianshu.com)

[8]OpenLayers地图渲染机制解析 - 简书 (jianshu.com)

posted @ 2022-07-21 21:49  当时明月在曾照彩云归  阅读(3691)  评论(0编辑  收藏  举报