• cesium 实现地图环境功能 - 雨,雪,雾特效


    需求背景

    需要实现天气模拟,日照模拟功能,提高三维实景效果

    解决效果

    Code

    在这里插入图片描述
    注意:我以下glsl文件时基于 webgl1.0,即cesium,创建球的时候,要指定 webgl 为 1.0 版本

    index.vue

    <- /**
    * @author: liuk
    * @date: 2024-07-15
    * @describe: 地图环境
    */ -->
    <template>
      <div class="map_scene-wrap">
        <div class="head_title_arrow">工具</div>
        <div class="second-level-heading">
          <span>天气模拟</span>
        </div>
        <div class="map_scene-content">
          <div @click="weatherClick(0)" :class="{ select: weatherItemSelectIndex === 0 }">晴天</div>
          <div @click="weatherClick(1)" :class="{ select: weatherItemSelectIndex === 1 }">下雨</div>
          <div @click="weatherClick(2)" :class="{ select: weatherItemSelectIndex === 2 }">下雪</div>
          <div @click="weatherClick(3)" :class="{ select: weatherItemSelectIndex === 3 }">大雾</div>
        </div>
        <div class="second-level-heading">
          <span>日照模拟</span>
        </div>
        <div class="ymfxClass">
          <div class="nowDate">{{ moment(new Date()).format('YYYY-MM-DD') }}</div>
          <el-slider :marks="{0: '0点',24: '24点'}" v-model="hour" :min="0" :max="24" @input="shadowSliderChange"></el-slider>
        </div>
      </div>
    </template>
    
    
    <script lang="ts" setup>
    import moment from "moment";
    import {onMounted, reactive, toRefs} from "vue";
    import {usemapStore} from "@/store/modules/cesiumMap";
    import FogEffect from "./fogEffect.ts"
    import rainGlsl from "./rain.glsl"
    import snowGlsl from "./snow.glsl"
    
    let lastStage, fogEffect
    
    const mapStore = usemapStore()
    const model = reactive({
      weatherItemSelectIndex: -1,
      hour: 12,
    })
    const {weatherItemSelectIndex, hour} = toRefs(model)
    
    onMounted(() => {
      fogEffect = new FogEffect({
        show: false,
        viewer,
        maxHeight: 40000, //大于此高度后不显示
        fogByDistance: new Cesium.Cartesian4(100, 0.0, 9000, 0.9),
        color: Cesium.Color.WHITE,
      });
      fogEffect.show = false
    })
    
    const weatherClick = (index) => {
      model.weatherItemSelectIndex = model.weatherItemSelectIndex === index ? -1 : index
      removeStage()
      switch (model.weatherItemSelectIndex) {
        case 0:
          model.hour = 12
          shadowSliderChange(12)
          break;
        case 1:
          showRain();
          break;
        case 2:
          showSnow();
          break;
        case 3:
          showfogEffect();
          break;
      }
    }
    // 地图逻辑
    const viewer = mapStore.getCesiumViewer();
    const showSnow = () => {
      lastStage = viewer.scene.postProcessStages.add(new Cesium.PostProcessStage({fragmentShader: snowGlsl}));
    }
    const showRain = () => {
      lastStage = viewer.scene.postProcessStages.add(new Cesium.PostProcessStage({fragmentShader: rainGlsl}));
    }
    const showfogEffect = () => {
      fogEffect.show = true;
    }
    const shadowSliderChange = (val) => {
      viewer.scene.globe.enableLighting = true
      // JulianDate 与北京时间 相差8小时
      const time = new Date(new Date().setHours(Number(val)) - 8 * 60 * 60 * 1e3);
      time.setHours(val);
      console.log(new Date(time).toLocaleString())
      viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(time.toISOString())// iso8601String
    }
    const removeStage = () => {
      viewer.scene.postProcessStages.remove(lastStage);
      fogEffect.show = false;
    }
    </script>
    
    <style lang="scss" scoped>
    .map_scene-wrap {
      align-items: flex-start;
      position: absolute;
      top: 70px;
      right: 65px;
      width: 300px;
      background: rgba(0, 0, 0, 0.6);
      border-radius: 4px;
      backdrop-filter: blur(2px);
      padding: 20px;
    
      .second-level-heading {
        margin-left: 10px;
        margin-top: 20px;
        font-size: 14px;
        color: #fff;
        position: relative;
        line-height: 1;
        padding-left: 10px;
    
        &::before {
          position: absolute;
          display: block;
          content: '';
          width: 3px;
          height: 70%;
          background-color: rgba(46, 165, 255, 1);
          left: 0;
          top: 50%;
          transform: translateY(-50%);
        }
    
        i {
          font-size: 16px;
          color: rgba(255, 255, 255, 0.8);
          cursor: pointer;
    
          &:hover {
            color: rgba(255, 255, 255, 1);
          }
        }
      }
    
      .map_scene-content {
        display: flex;
        flex-wrap: wrap;
        font-family: PingFang SC Regular;
        padding-top: 10px;
    
        & > div {
          padding: 4px 10px;
          margin-right: 10px;
          cursor: pointer;
          color: #fff;
          font-size: 13px;
          background: rgba(46, 165, 255, 0.3);
          border: 1px solid #2ea5ff;
    
          &:not(:first-child) {
            margin-left: 8px;
          }
        }
    
        .select {
          background: #2ea5ff;
          border: 1px solid #2ea5ff;
        }
      }
    
      .ymfxClass {
        margin-left: 20px;
    
        .nowDate {
          text-align: right;
          font-size: 12px;
          font-family: SourceHanSansCN-Regular, SourceHanSansCN;
          font-weight: 400;
          color: #2ea5ff;
        }
    
        ::v-deep .el-slider {
          .el-slider__button {
            width: 13px;
            height: 13px;
            position: relative;
            top: -1px;
          }
    
          .el-slider__runway {
            height: 4px;
            background: rgb(255, 255, 255, 0.3);
    
            .el-slider__bar {
              height: 100%;
              color: rgba(46, 165, 255, 1);
            }
    
            .el-slider__marks-text {
              font-size: 12px;
              font-weight: 400;
              color: #2ea5ff;
            }
          }
    
          .el-slider__stop {
            display: none;
            height: 4px;
            background: rgb(255, 255, 255, 0.3);
          }
        }
      }
    }
    </style>
    

    fogEffect.ts

    /**
     * @author: liuk
     * @date: 2024-07-14
     * @describe:场景雾效果
     * @by:根据深度图的深度值,对片元进行不同程度的模糊
     */
    
    import * as Cesium from "cesium";
    import FogFS from "./fog.glsl";
    
    export default class FogEffect {
        //========== 构造方法 ==========
        constructor(options) {
            this.viewer = options.viewer;
    
            this.fogByDistance = Cesium.defaultValue(
                options.fogByDistance,
                new Cesium.Cartesian4(10, 0.0, 1000, 0.9)
            ); //雾强度
            this.color = Cesium.defaultValue(options.color, Cesium.Color.WHITE); //雾颜色
    
            this._show = Cesium.defaultValue(options.show, true);
            this._maxHeight = Cesium.defaultValue(options.maxHeight, 9000);
    
            this.init();
        }
    
        //========== 对外属性 ==========
        //是否开启效果
        get show() {
            return this._show;
        }
    
        set show(val) {
            this._show = Boolean(val);
            this.FogStage.enabled = this._show;
        }
    
        //========== 方法 ==========
    
        init() {
            var that = this;
    
            this.FogStage = new Cesium.PostProcessStage({
                fragmentShader: FogFS,
                uniforms: {
                    fogByDistance: function () {
                        return that.fogByDistance;
                    },
                    fogColor: function () {
                        return that.color;
                    }
                },
                enabled: this._show
            });
            this.viewer.scene.postProcessStages.add(this.FogStage);
    
            //加控制,只在相机高度低于一定高度时才开启本效果
            this.viewer.scene.camera.changed.addEventListener(this.camera_changedHandler, this);
        }
    
        camera_changedHandler(event) {
            if (this.viewer.camera.positionCartographic.height < this._maxHeight) {
                this.FogStage.enabled = this._show;
            } else {
                this.FogStage.enabled = false;
            }
        }
    
        //销毁
        destroy() {
            this.viewer.scene.camera.changed.removeEventListener(this.camera_changedHandler, this);
            this.viewer.scene.postProcessStages.remove(this.FogStage);
    
            //删除所有绑定的数据
            for (let i in this) {
                delete this[i];
            }
        }
    }
    
    

    rain.glsl

    uniform sampler2D colorTexture;//输入的场景渲染照片
    varying vec2 v_textureCoordinates;
    
    float hash(float x){
        return fract(sin(x*133.3)*13.13);
    }
    
    void main(void){
    
        float time = czm_frameNumber / 240.0;
        vec2 resolution = czm_viewport.zw;
    
        vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);
        vec3 c=vec3(.6,.7,.8);
    
        float a=-.4;
        float si=sin(a),co=cos(a);
        uv*=mat2(co,-si,si,co);
        uv*=length(uv+vec2(0,4.9))*.3+1.;
    
        float v=1.-sin(hash(floor(uv.x*100.))*2.);
        float b=clamp(abs(sin(20.*time*v+uv.y*(5./(2.+v))))-.95,0.,1.)*20.;
        c*=v*b; //屏幕上雨的颜色
    
        gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(c,1), 0.5); //将雨和三维场景融合
    }
    

    snow.glsl

    uniform sampler2D colorTexture; //输入的场景渲染照片
    varying vec2 v_textureCoordinates;
    
    float snow(vec2 uv,float scale){
        float time = czm_frameNumber / 60.0;
        float w=smoothstep(1.,0.,-uv.y*(scale/10.));
        if(w<.1)return 0.;
        uv+=time/scale;
        uv.y+=time*2./scale;
        uv.x+=sin(uv.y+time*.5)/scale;
        uv*=scale;
        vec2 s=floor(uv),f=fract(uv),p;
        float k=3.,d;
        p=.5+.35*sin(11.*fract(sin((s+p+scale)*mat2(7,3,6,5))*5.))-f;
        d=length(p);k=min(d,k);
        k=smoothstep(0.,k,sin(f.x+f.y)*0.01);
        return k*w;
    }
    
    void main(void){
        vec2 resolution = czm_viewport.zw;
        vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);
        vec3 finalColor=vec3(0);
        float c = 0.0;
        c+=snow(uv,10.);
        c+=snow(uv,8.);
        c+=snow(uv,6.);
        c+=snow(uv,5.);
        finalColor=(vec3(c)); //屏幕上雪的颜色
        gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(finalColor,1), 0.5);  //将雪和三维场景融合
    
    }
    
    

    fog.glsl

    float getDistance(sampler2D depthTexture, vec2 texCoords)
    {
        float depth = czm_unpackDepth(texture2D(depthTexture, texCoords));
        if (depth == 0.0) {
            return czm_infinity;
        }
        vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
        return -eyeCoordinate.z / eyeCoordinate.w;
    }
    float interpolateByDistance(vec4 nearFarScalar, float distance)
    {
        float startDistance = nearFarScalar.x;
        float startValue = nearFarScalar.y;
        float endDistance = nearFarScalar.z;
        float endValue = nearFarScalar.w;
        float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);
        return mix(startValue, endValue, t);
    }
    vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor)
    {
        return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a);
    }
    uniform sampler2D colorTexture;
    uniform sampler2D depthTexture;
    uniform vec4 fogByDistance;
    uniform vec4 fogColor;
    varying vec2 v_textureCoordinates;
    void main(void)
    {
        float distance = getDistance(depthTexture, v_textureCoordinates);
        vec4 sceneColor = texture2D(colorTexture, v_textureCoordinates);
        float blendAmount = interpolateByDistance(fogByDistance, distance);
        vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * blendAmount);
        gl_FragColor = alphaBlend(finalFogColor, sceneColor);
    }
    
    
  • 相关阅读:
    QTday3
    一文弄懂JUnit5相关注解
    面对繁杂的工作时,我总是走神,效率非常低,有什么解决建议
    电商数仓整体理解
    Oracle Data Redaction和Oracle Data Pump
    matplotlib python 画图介绍
    【MySQL】如何配置复制拓扑?
    clickhouse的多路径存储策略
    spark源码阅读总纲
    Eureka Series : USB / UART / TTL / 232 / 485 Debuger
  • 原文地址:https://blog.csdn.net/hr_beginner/article/details/140429911