在高德地图实现后期效果

介绍

最近在做可视化图层开发的时候,发现我们自己开发的图层一些优秀的案例比起来,总是有一定的差距。差了后期效果合成环节,就比如个人晒图前忘了用美图秀秀修图。于是花了些时间研究了高德地图JSAPI2.0和GLCustomLayer,探索如何将后期特效接入到3D图层中。

后期特效其实有点类似照片的后期滤镜处理,是对渲染结果的二次处理,可以实现发光、模糊、色调调整、镜头暗角、模拟环境光遮蔽等各种效果,为了方便理解,下面的讲解我将以辉光效果为例,学会了一种其他效果思路类似。

方案调研

Three官方提供了非常简单的方法实现后期特效,貌似仅需要完成以下两个步骤就可以完成我们想要的需求,代码也非常清晰简单:

jsx 复制代码
import * as THREE from 'three'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing//UnrealBloomPass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'

...

// 1.在图层初始化完成后,创建效果合成器
onLayerInit(){
	const { scene, camera, renderer } = this

    const renderScene = new RenderPass(scene, camera)

    // 后期泛光特效
    bloomPass = new UnrealBloomPass(new THREE.Vector2(this.container.clientWidth, this.container.clientHeight), 1, 0, 0)
    bloomPass.threshold = params.threshold
    bloomPass.strength = params.strength
    bloomPass.radius = params.radius

    composer = new EffectComposer(renderer)
    // 以下代码会遮盖地图
    composer.addPass(renderScene)
    composer.addPass(bloomPass)
}

// 2.更新合成器
onRender () {
  if (composer) {
    composer.render()
  }
}

本以为这样做就可以开心收工了,燃鹅事情并没有那么简单,把这套方案移入高德的GLCustomLayer中,出现了这样的情况,后期效果直接把地图底图盖住了。

出现这种情况的原因是实现辉光效果而编写的着色器,它会直接修改整个画面的alpha通道而导致透明效果丢失,因此需要单独修改UnrealBloomPass.js。

然而光是这样还不够,经过各种尝试,仍无法直接在GLCustomLayer上解决地图被遮盖的问题,后来咨询了高德地图开发团队的技术大佬,他给我的建议是后期效果层独立展示,于是就沿着这个思路进行了第二轮尝试。

这里面有几个关键步骤是必须的:

  1. 修改UnrealBloomPass着色器代码
  2. 使用输出通道new OutputPass()置于特效通道的后面
  3. 在customLayer图层中,每次渲染就更新特效合成器EffectComposer

由于我这边是不希望之前开发的可视化图层做太多的修改去迁就这个后期效果的,也有对性能较差的终端机器优雅降级的考虑,索性把后期效果独立为EffectLayer层,以方便灵活地装载或剥离,最终实现了这个效果。

实现步骤

  1. 修改 UnrealBloomPass.js,由于这个文件在npm包中不能随意修改,我另外写了一个UnrealBloomPass1 继承并覆盖了UnrealBloomPass的方法

    jsx 复制代码
    import * as THREE from 'three'
    import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
    
    class UnrealBloomPass1 extends UnrealBloomPass {
      constructor (resolution, strength, radius, threshold) {
        super(resolution, strength, radius, threshold)
      }
    
      getSeperableBlurMaterial (kernelRadius) {
         ...
          fragmentShader:
            `#include <common>
    				varying vec2 vUv;
    				uniform sampler2D colorTexture;
    				uniform vec2 invSize;
    				uniform vec2 direction;
    				uniform float gaussianCoefficients[KERNEL_RADIUS];
    
    				void main() {
    					float weightSum = gaussianCoefficients[0];
    					vec3 diffuseSum = texture2D( colorTexture, vUv ).rgb * weightSum;
    					float alphaSum;
    					for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
    						float x = float(i);
    						float w = gaussianCoefficients[i];
    						vec2 uvOffset = direction * invSize * x;
    						vec4 sample1 = texture2D( colorTexture, vUv + uvOffset );
    						vec4 sample2 = texture2D( colorTexture, vUv - uvOffset );
    						diffuseSum += (sample1.rgb + sample2.rgb) * w;
    						alphaSum += (sample1.a + sample2.a) * w; //
    						weightSum += 2.0 * w;
    					}
    					// gyrate: overwrite this line for alpha pass
    					// gl_FragColor = vec4(diffuseSum/weightSum, 1.0);
    					gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);
    				}`
        })
      }
    }
    
    export { UnrealBloomPass1 }
  2. 编写EffectLayer

    jsx 复制代码
    import * as THREE from 'three'
    import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
    import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
    import { UnrealBloomPass1 } from '../plugins/three/examples/jsm/postprocessing/UnrealBloomPass.js'
    import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'
    import _ from 'lodash'
    
    class EffectLayer {
      
      // 此处省去一些内部变量  
    
      _style = {
        // 光照强度阈值
        threshold: 0.0,
        // 泛光强度
        strength: 1.0,
        // 泛光半径
        radius: 1.5
      }
      
    	/**
       * 创建一个实例
       * @param {Object} config
       * @param {Layer} config.layer 目标图层,要求是Layer的相关子类
       * @param {Number} [config.zIndex=120] 图层的层级
       * @param {EffectStyle} [config.style] 后期特效的配置项
       */
      constructor (config) {
        const conf = _.merge(this._conf, config)
        this._style = _.merge(this._style, conf.style)
    
        if (!conf.layer.scene || !conf.layer.camera) {
          console.error('缺少场景和相机')
          return
        }
        this.init()
      }
    
      init () {
        this.createLayer()
        this.addEffect()
      }
    }
  3. 创建自定义图层customLayer

    jsx 复制代码
    createLayer () {
        const canvas = document.createElement('canvas')
        this._customLayer = new AMap.CustomLayer(canvas, {
          zooms: [3, 22],
          zIndex: this._conf.zIndex,
          alwaysRender: true
        })
    
        this._canvas = canvas
      }
  4. 创建特效合成器

    jsx 复制代码
    addEffect () {
        const { scene, camera, container, renderer, map } = this._conf.layer
        const { clientWidth, clientHeight } = container
    
        // 创建渲染器
        const effectRender = new THREE.WebGLRenderer({
          canvas: this._canvas,
          alpha: true,
          antialias: false,
          stencil: false,
          depth: false
        })
        // renderer.setClearColor(0xff0000);
        effectRender.autoClear = false
        effectRender.setSize(clientWidth, clientHeight)
    
        // 后期效果
        const renderScene = new RenderPass(scene, camera)
    
        // 后期辉光特效
        const bloomPass = new UnrealBloomPass1(new THREE.Vector2(clientWidth, clientHeight), 1, 0, 0)
        bloomPass.clear = false
    
        // 输出通道
        const outputPass = new OutputPass()
        outputPass.clear = false
    
        this.updatePass()
    
        const composer = new EffectComposer(effectRender)
        composer.addPass(renderScene)
        composer.addPass(bloomPass)
        composer.addPass(outputPass)
    
        this._composer = composer
        this._bloomPass = bloomPass
    
        this._customLayer.render = function () {
          if (composer) {
    				// 每次渲染就更新特效合成器
            composer.render()
          }
        }
    
        map.add(this._customLayer)
      }
    
      updatePass() {
        const {_bloomPass} = this
        if (_bloomPass) {
          _bloomPass.threshold = this._style.threshold
          _bloomPass.strength = this._style.strength
          _bloomPass.radius = this._style.radius
        }
        // 添加其他特效通道...
       }
  5. 使用EffectLayer

    jsx 复制代码
    //之前编写的可视化图层
    const layer = new GLlayers.POI3dLayer({
      map: getMap(),
      zooms: [10, 22]
    })
    
    layer.on('complete', (layer) => {
       let effectLayer = new GLlayers.EffectLayer({
          layer: layer, //把图层传入effectLayer
          style:{
            threshold: 0.0,
            strength: 1.0,
            radius: 0.5,
          }
       })
    })

注意:以上方案three.js版本为0.157, 该版本对three/example/jsm/postprocessing目录中的后期效果通道相关文件做了较多调整,如果是用之前的three.js版本,修改内容可能有所不同。

至此我们就可以在之前的可视化图层基础上,加入几行代码实现辉光效果,以下是挑选一部分图层加上EffectLayer之后的效果,肉眼可见还是有很明显区别的。当然在使用过程中也发现了个别图层原有的问题需要做进一步优化。

待解决问题

使用独立图层展示后期特效层有个明显缺点,无法关联默认基本图层的场景要素深度信息,最主要的影响是高德的建筑白模图层和自定义可视化图层的远近遮挡关系会丢失,导致可视化图层永远在最前面。比如下面这个城市主要道路的辉光效果,这个是需要后面花时间去解决的,写这篇文章的时候又找到几个方案,有时间再试一把,毕竟上面留给我的时间不多了。

相关链接

three.js后期处理

three.js效果合成器文档和示例

实现模型材质局部辉光效果和解决辉光影响场景背景图显示的问题

Three.js带Depth实现分区辉光

相关推荐
用户41429296072397 小时前
淘宝实时商品API接口:采集竞品商品详情页的价格、SKU 规格、库存数量、卖点文案、图文内容、售后政策(运费、退换货规则)、评价核心标签
数据挖掘·数据分析·数据可视化
杨超越luckly1 天前
基于 Overpass API 的城市电网基础设施与 POI 提取与可视化
python·数据可视化·openstreetmap·电力数据·overpass api
用户5962585736061 天前
【征文计划】当AI Glasses成为你的“植物百科全书”
数据可视化
HsuHeinrich4 天前
利用面积图探索历史温度的变化趋势
python·数据可视化
CodeCraft Studio7 天前
空间天气监测,TeeChart助力实现太阳活动数据的可视化分析
信息可视化·数据挖掘·数据分析·数据可视化·teechart·科研图表·图表库
FIT2CLOUD飞致云7 天前
安全漏洞修复,API数据源支持添加时间戳参数,DataEase开源BI工具v2.10.17 LTS版本发布
开源·数据可视化·dataease·bi·数据大屏
图扑可视化8 天前
图扑 HT 智慧汽车展示平台全自研技术方案
汽车·数据可视化·组态监控·汽车展示
Highcharts.js9 天前
Highcharts开发解析:从数据可视化到用户体验的全面指南
信息可视化·前端框架·数据可视化·ux·highcharts·交互图表
数字冰雹10 天前
为城市治理装上“数字引擎”
中间件·数据可视化
Highcharts.js10 天前
学习 Highcharts 可视化开发的有效途径
学习·数据可视化·highcharts·图表开发·可视化开发