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

news2024/11/13 23:33:33

  • 需求背景
  • 解决效果
  • Code
    • index.vue
    • fogEffect.ts
    • rain.glsl
    • snow.glsl
    • fog.glsl

需求背景

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

解决效果

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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1927018.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ES的使用示例

1.安装 ES的安装对springboot的版本配置要求很高&#xff0c;需要根据如下的目录下载对应的版本。 查看自己项目所使用的springboot和spring的版本&#xff0c;对应下载文件。 下载链接地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-…

微软GraphRAG原理介绍(附带部分源码)

我们前几天写了一篇文章&#xff0c;简单跑了一下GraphRAG看了下效果&#xff0c;没看过这篇文章的可以看下https://www.luxinfeng.top/article/动手实操微软开源的GraphRAG。今天我们介绍一下GraphRAG的实现原理&#xff0c;关于实验对比的内容&#xff0c;我会在下一篇文章中…

48V电源架构解析

48V电源架构解析 48V系统的诞生 汽车在1918年引入蓄电池&#xff0c;到1920年逐渐普及&#xff0c;当时的电池电压是6V。后来&#xff0c;随着内燃机排量的增加以及高压缩比内燃机的出现&#xff0c;6V系统已经不能满足需求&#xff0c;于是在1950年引入了12V系统。大多数汽车…

【python学习】标准库之数学相关math库的定义、功能、使用场景、代码示例和第三方数学相关库NumPy

引言 math模块是Python标准库的一部分&#xff0c;它提供了一系列基本的数学函数和常数。这些函数和常数对于日常的数学运算非常有用&#xff0c;例如计算平方根、计算余弦值等。 文章目录 引言一、math的定义二、math的功能2.1 基本的数学运算2.2 数学常数2.3 随机数 三、math…

八臂-聚乙二醇-生物素;8ARM-PEG-Biotin

一、基本信息 名称&#xff1a;八臂PEG生物素、八臂-聚乙二醇-生物素、Octa-arm PEG Biotin、8ARM-PEG-Biotin 结构&#xff1a;具有八个分支的PEG链&#xff0c;每个分支末端连接生物素分子 状态&#xff1a;固体/粉末/溶液&#xff0c;具体取决于产品规格和存储条件 纯度&…

论文去AI痕秘籍:轻松几步,守护你的学术原创性

如何有效降低AIGC论文的重复率&#xff0c;也就是我们说的aigc如何降重&#xff1f;AIGC疑似度过高确实是个比较愁人的问题。如果你用AI帮忙写了论文&#xff0c;就一定要在交稿之前做一下AIGC降重的检查。一般来说&#xff0c;如果论文的AIGC超过30%&#xff0c;很可能会被判定…

Qt|QTreewidget类下函数qt助手详解说明示例(二)

上篇&#xff1a;Qt|QTreewidget类下函数qt助手详解说明示例&#xff08;一&#xff09; 该系列持续更新&#xff0c;喜欢请一键三连&#xff0c;一起学习进步&#xff0c;升职加薪&#xff0c;感谢各位大佬。 QT5.14.2 参考官方QT助手 Kimi辅助说明 文章目录 insertTopLevelI…

浅学三次握手

数据要完成传输&#xff0c;必须要建立连接。由于建立TCP连接的过程需要来回3次&#xff0c;所以&#xff0c;将这个过程形象的叫做三次握手。 结合上面的图来看更清楚。 先说三次握手吧&#xff0c;连接是后续数据传输的基础。就像我们打电话一样&#xff0c;必须保证我和对方…

工作是为了什么

如果经常和总监及以上的领导聊天&#xff0c;就会发现他们与咱们一线程序员的最大不同&#xff0c;并不是编程技能多高深精通&#xff0c;而是分析问题、认知世界的方式。 程序员只关注工作领域的“一亩三分地”&#xff0c;实质上是放弃了自己成长的机会&#xff0c;在工作的…

软件测试——非功能测试

工作职责&#xff1a; 1.负责产品系统测试&#xff0c;包括功能测试、性能测试、稳定性测试、用户场景测试、可靠性测试等。 2.负责测试相关文档的编写&#xff0c;包括测试计划、测试用例、测试报告等。 3.负责自动化测试框架、用例的维护。 岗位要求&#xff1a; 1.熟练…

解决mysql,Navicat for MySQL,IntelliJ IDEA之间中文乱码

使用软件版本 jdk-8u171-windows-x64 ideaIU-2021.1.3 mysql-essential-5.0.87-win32 navicat8_mysql_cs 这个问题我调试了好久&#xff0c;网上的方法基本上都试过了&#xff0c;终于是解决了。 三个地方结果都不一样。 方法一 首先大家可以尝试下面这种方法&#xff1a…

53 传输层

作用&#xff1a;负责数据能够从发送端传输接收端 再谈端口号 端口号&#xff08;port&#xff09;标识了一个主机上进行通信的不同的应用程序 在TCP/IP协议中&#xff0c;用“源IP”&#xff0c;“源端口号”&#xff0c;“目的IP”&#xff0c;“目的端口号”&#xff0c…

陶晶驰串口屏与arduino uno通信,远程控制arduino 2560上的LED

一 材料清单 arduino uno 1个 arduino 2560 1个 nrf24l01 2个 陶晶驰串口屏 1个 二 本文目的 通过串口屏触摸按键远程控制arduino 2560上的LED 点亮。 三 硬件接线 3.1 发射端接线 3.1.1uno和发射模块接线 nRF24L01 与Arduino UNO接线如…

你也想做一个Elemen-ui吧!!!——Blueの前端路

目录 前言 diglog组件 准备工作&#xff1a; 在diglog.vue中模仿element-ui搭建diglog框架 该组件需要完成的任务&#xff1a; title diglog.vue代码&#xff1a; App.vue代码&#xff1a; 效果&#xff1a; 自定义dialog的宽度和距离顶部的 App.vue代码&#xff1a;…

Windows 下 VMamba 安装教程(无需更改base环境中的cuda版本且可加速)

导航 Mamba 及 Vim 安装问题参看本人之前博客&#xff1a;Mamba 环境安装踩坑问题汇总及解决方法Windows 下 Mamba 的安装参看本人之前博客&#xff1a;Window 下Mamba 环境安装踩坑问题汇总及解决方法 &#xff08;无需绕过selective_scan_cuda&#xff09;Linux 下VMamba 安…

前端面试题(JS篇三)

一、|| 和 && 操作符的返回值&#xff1f; || 和 && 首先会对第一个操作数执行条件判断&#xff0c;如果其不是布尔值就先进行 ToBoolean 强制类型转换&#xff0c;然后再执行条件判断。 对于 || 来说&#xff0c;如果条件判断结果为 true 就返回第一个操作数的…

磁环编码器原理

目录 概述 1 编码器介绍 2 实现原理介绍 2.1 磁环功能分析 2.2 硬件实现方式 3 编码器参数 3.1 编码器精度 3.2 影响编码器精度的因素 4 角度计算方法 4.1 单对极编码器 4.2 磁游标编码器 4.2.1 游标方案实现原理 4.2.2 一个实例磁环分析 5 磁刻线编码器 概述 本…

算法题-二叉树

二叉树 二叉树的理论知识 二叉树的种类 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的节点和度为2的节点&#xff0c;并且度为0的节点在同一层&#xff0c;则此二叉树为满二叉树&#xff08;深度为k&#xff0c;有2^k-1个节点的二叉树&#xff09;。 完全二叉…

PostgreSQL 中如何解决因长事务阻塞导致的其他事务等待问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何解决因长事务阻塞导致的其他事务等待问题&#xff1f;一、了解长事务阻塞的原因&…

降Compose十八掌之『利涉大川』| Canvas

公众号「稀有猿诉」 原文链接 降Compose十八掌之『利涉大川』| Canvas 任何一个GUI框架都会提供大量的预定义的UI部件&#xff0c;让开发者构建UI页面&#xff0c;但有些时候预定义的部件无法满足需求&#xff0c;这时就需要定制&#xff0c;甚至是自定义绘制的内容。对…