用cesium的后期处理实现类似three.js中的spotLight效果

一、背景

当我们用cesium来做一些灯光效果的时候,比如扫光效果啥的,会发现cesium没有像three.js或者babylon.js那样自定义灯光,cesium只有直射光也就是太阳光,如果要是实现类似three.js的灯光效果怎么办呢?

一般是有三种效果:

一、片元着色器直接着色,这个无论在那个引擎都是一样的,再细分一下有两种,一种是通过uv坐标来计算光照;另一种加入三维坐标,例如要做一个扫光的效果,扫光的中心需要自己定义,也就是将笛卡尔坐标从cpu传到GPU,这不展开说,先挖个坑;但是这个有一个缺点,因为这个是当前对象表面着色的,没办法影响到别的对象;试想,当灯光打下来了,为什么只有你被照亮了,旁边的咋办,这个灯光太偏心了;那没办啊,只能给旁边的对象也用同样的方式着色了

二、通过后期处理,在这里其实和上面说的通过uv着色有点点像,但是也有差别,它是在原来对象的表面颜色加上灯光的颜色,当然也可以直接纯颜色代替对象面的颜色,但是这个就不是灯光了,在cesium因为不需要灯光也能看到对象,所以就是会让被灯光找到的物体更加亮了,怎么样算是被找到呢,这就是我们要计算的了,带会让在代码里说明

三、通过模板、这个了解不多,不是很懂也没用过,不过据说可以,在three.js中

本次是采用的时候后期处理来实现,但是坐标也是从外面传进去的也是第一点说的有点像,至于里面的坐标转换部分,也就是将坐标传进去GPU之前做的转换我是参考,忘了那个大佬的,虽然我想尊重原创,但是我已经不知道哪个是原创的了,哈哈哈,感觉网上很多都一样,所以那些坐标转换部分我就不改了,尊重原创,重要事着色器部分哈

import * as Cesium from 'cesium'
// 圆扩散
class CircleDiffusion {
    viewer: any
    lastStageList: Array<any>
    constructor(viewer: any) {
      this.viewer = viewer
      this.lastStageList = []
    }
    add(
      position: Array<number>,
      radio = 300
    ) {
      this.lastStageList.push(
        this.showCircleScan(position, radio)
      )
    }
    clear() {
      this.lastStageList.forEach((ele: any) => {
        this.clearScanEffects(ele)
      })
      this.lastStageList = []
    }
    /**
     * 圆扩散
     * @param {*} position  扫描中心 如[117.270739,31.84309,32]
     * @param {*} scanColor 扫描颜色 如"rgba(0,255,0,1)"
     * @param {*} maxRadius 扫描半径,单位米 如1000米
     * @param {*} duration 持续时间,单位毫秒 如4000
     */
    showCircleScan(
      position: Array<number>,
      maxRadius: number,
    ) {
      const cartographicCenter = new Cesium.Cartographic(
        Cesium.Math.toRadians(position[0]),
        Cesium.Math.toRadians(position[1]),
        position[2]
      )
      const lastStage = this._addCircleScanPostStage(
        cartographicCenter,
        maxRadius,
      )
      return lastStage
    }
    /**
     * 添加扩散效果扫描线
     * @param {*} cartographicCenter  扫描中心
     * @param {*} maxRadius 扫描半径
     */
    _addCircleScanPostStage(
      cartographicCenter: any,
      maxRadius = 300,
    ) {
      const _Cartesian3Center =
        Cesium.Cartographic.toCartesian(cartographicCenter)
      const _Cartesian4Center = new Cesium.Cartesian4(
        _Cartesian3Center.x,
        _Cartesian3Center.y,
        _Cartesian3Center.z,
        1
      )
      const _CartograhpicCenter1 = new Cesium.Cartographic(
        cartographicCenter.longitude,
        cartographicCenter.latitude,
        cartographicCenter.height + 500
      )
      const _Cartesian3Center1 =
        Cesium.Cartographic.toCartesian(_CartograhpicCenter1)
      const _Cartesian4Center1 = new Cesium.Cartesian4(
        _Cartesian3Center1.x,
        _Cartesian3Center1.y,
        _Cartesian3Center1.z,
        1
      )
      const _scratchCartesian4Center = new Cesium.Cartesian4()
      const _scratchCartesian4Center1 = new Cesium.Cartesian4()
      const _scratchCartesian3Normal = new Cesium.Cartesian3()
      const _this = this
      const ScanPostStage = new Cesium.PostProcessStage({
        fragmentShader: _this._getScanSegmentShader(),
        uniforms: {
          u_scanCenterEC: function() {
            const temp = Cesium.Matrix4.multiplyByVector(
              _this.viewer.camera._viewMatrix,
              _Cartesian4Center,
              _scratchCartesian4Center
            )
            return temp
          },
          u_scanPlaneNormalEC: function() {
            const temp = Cesium.Matrix4.multiplyByVector(
              _this.viewer.camera._viewMatrix,
              _Cartesian4Center,
              _scratchCartesian4Center
            )
            const temp1 = Cesium.Matrix4.multiplyByVector(
              _this.viewer.camera._viewMatrix,
              _Cartesian4Center1,
              _scratchCartesian4Center1
            )
            _scratchCartesian3Normal.x = temp1.x - temp.x
            _scratchCartesian3Normal.y = temp1.y - temp.y
            _scratchCartesian3Normal.z = temp1.z - temp.z
            Cesium.Cartesian3.normalize(
              _scratchCartesian3Normal,
              _scratchCartesian3Normal
            )
            return _scratchCartesian3Normal
          },
          u_radius: function() {
            return maxRadius
          },
          u_scanColor: new Cesium.Color(0.2,0.25,0.3,0.1),
        },
      })
      this.viewer.scene.postProcessStages.add(ScanPostStage)
      return ScanPostStage
    }
    /**
     * 扩散效果Shader
     */
    _getScanSegmentShader() {
      const scanSegmentShader =
        ` uniform sampler2D colorTexture;
          uniform sampler2D depthTexture;
          in vec2 v_textureCoordinates;
          uniform vec4 u_scanCenterEC;
          uniform vec3 u_scanPlaneNormalEC;
          uniform float u_radius;
          uniform vec4 u_scanColor;
          vec4 toEye(in vec2 uv, in float depth){
            vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));
            vec4 posInCamera = czm_inverseProjection * vec4(xy, depth, 1.0);
            posInCamera =posInCamera / posInCamera.w;
            return posInCamera;
          }
          vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point){
              vec3 v01 = point - planeOrigin;
              float d = dot(planeNormal, v01) ;
              return (point - planeNormal * d);
          }
          float getDepth(in vec4 depth){
              float z_window = czm_unpackDepth(depth);
              z_window = czm_reverseLogDepth(z_window);
              float n_range = czm_depthRange.near;
              float f_range = czm_depthRange.far;
              return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
          }
          
          void main(){
              out_FragColor = texture(colorTexture, v_textureCoordinates);
              float depth = getDepth(texture(depthTexture, v_textureCoordinates));
              vec4 viewPos = toEye(v_textureCoordinates, depth);
              vec3 scanCenterEC = u_scanCenterEC.xyz + vec3(cos(czm_frameNumber * 0.05) * 300.0,sin(czm_frameNumber * 0.05) * 300.0, sin(czm_frameNumber * 0.01) * 300.0);
              vec3 prjOnPlane = pointProjectOnPlane(u_scanPlaneNormalEC.xyz, scanCenterEC.xyz, viewPos.xyz);
              float dis = length(prjOnPlane.xyz - scanCenterEC.xyz);
              if(dis < u_radius){
                out_FragColor += pow(150.0/max(dis,150.0),2.0) * u_scanColor;
              }
          }
        `
      return scanSegmentShader
    }
    /**
     * 清除特效对象
     * @param {*} lastStage 特效对象
     */
    clearScanEffects(lastStage: any) {
      this.viewer.scene.postProcessStages.remove(lastStage)
    }
  }
  
  export default CircleDiffusion

在着色器中,给出了半径和角度还有根据光线到中心点的衰减是通过一个指数来实现的,可以自定义优化一下,我去的指数为2,可以根据实际项目需要进行更改

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值