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

  • 需求背景
  • 解决效果
  • 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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机STC89C52和GSM实现的远程拨号开锁设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于单片机STC89C52和GSM实现的远程拨号开锁设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 摘要 仿真图 单片机系统流程图 实物图 代码 系统论文 资源下载 摘要 本文介…

在 Windows 上开发.NET MAUI 应用_2.生成你的第一个应用

先决条件 Visual Studio 2022 17.8 或更高版本&#xff0c;并安装了 .NET Multi-platform App UI 工作负载。 可参考上一篇文章&#xff1a;http://t.csdnimg.cn/n38Yy 创建应用 1.启动 Visual Studio 2022。 在开始窗口中&#xff0c;单击“创建新项目”以创建新项目&#…

java:aocache 与Spring Aop兼容问题

本文适用于所有AspectJ与Spring AOP混用的场景。 Spring AOP 是基于动态代理的实现AOP&#xff0c;基于 JDK代理和CGLib代理实现运行时织入&#xff08;runtime weaving&#xff09;。 Spring AOP的切面定义沿用了ASpectJ的注解体系&#xff0c;所以在Spring体系中注解定义切面…

Jenkins安装nodeJs环境

首先插件市场安装nodeJS插件&#xff0c;我这里已经安装了&#xff0c;没安装的话在 Available plugins 中搜索安装 安装完成后需要下载需要的nodejs版本 新增完成就可以在构建的时候选择当前版本号了

【python虚拟环境管理】【mac m3】 使用pipx安装poetry

文章目录 一. 安装 pipx二. 安装Poetry1. 安装2. advanced 操作 官网文档&#xff1a;https://python-poetry.org/docs/ pipx介绍文档&#xff1a;https://blog.51cto.com/u_15064632/2570626 一. 安装 pipx pipx 用于全局安装 Python 命令行应用程序&#xff0c;同时在虚拟环…

Linux 线程初步解析

1.线程概念 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列。在linux中&#xff0c;由于线程和进程都具有id,都需要调度等等相似性&#xff0c;因此都可以用PCB来描述和控制,线程含有PCB&am…

uniapp开发钉钉小程序流程

下载开发工具 1、小程序开发工具 登录钉钉开发平台&#xff0c;根据自己的需求下载合适的版本&#xff0c;我这里下载的是Windows &#xff08;64位&#xff09;版本 小程序开发工具 - 钉钉开放平台 2、HBuilder X HBuilderX-高效极客技巧 新建项目及相关配置 新建项目 …

云动态摘要 2024-07-16

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起&#xff01; [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造&…

智慧煤矿:AI视频智能监管解决方案引领行业新变革

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到各个行业&#xff0c;为传统产业的转型升级提供了强大的动力。在煤矿行业中&#xff0c;安全监管一直是一个重要的议题。为了提高煤矿的安全生产水平&#xff0c;降低事故发生率&#xff0c;智…

远程访问及控制(ssh)

目录 一、OpenSSH服务器 1.1 SSH&#xff08;Secure Shell&#xff09;协议 1.2 OpenSSH 2.1 SSH原理 2.1 1 公钥传输原理 2.1.2 加密原理 2.1.3 SSHD服务的两种验证方式 二、SSH命令中的基本操作 2.1 构建密钥对验证的SSH 2.1.1 公钥和密钥的关系 2.1.2 构建密钥对…

linux虚拟机主ip地址:网络信息不可用(没IP)

第一种情况其它博主写的很详细 就是在 /etc/sysconfig/network-scripts/ifcfg-ens33 的onbootno 改为 yes 然后重启 第二种就是我遇到的&#xff0c;是因为服务没有启动 首先winr打开搜索 然后搜索service.msc 把这两项手工右键开启即可&#xff0c;然后重启虚拟机&a…

安卓笔记1-Retrofit2请求自定义接口

1、整体功能概述 安卓项目中使用Retrofit2实现和自定义接口的网络交互&#xff0c;通过Postman模拟服务端&#xff0c;创建自定义接口。 作用 前后端开发进度对不齐时&#xff0c;客户端可利用本功能模拟测试数据。备忘。 缺点 retrofit模拟接口需要配置响应数据类&#xff…

STM32入门开发操作记录(二)——LED与蜂鸣器

目录 一、工程模板二、点亮主板1. 配置寄存器2. 调用库函数 三、LED1. 闪烁2. 流水灯 四、蜂鸣器 一、工程模板 参照第一篇&#xff0c;新建工程目录ProjectMould&#xff0c;将先前打包好的Start&#xff0c;Library和User文件^C^V过来&#xff0c;并在Keil5内完成器件支持包的…

可视化工具选择指南:助力企业数字化转型和新质生产力发展

随着信息技术的快速发展和新质生产力概念的兴起&#xff0c;可视化工具在各个行业中的作用日益凸显。这些工具不仅能够帮助用户更直观地理解和分析数据&#xff0c;还能提升团队的协作效率和决策质量。 在当今数字化转型迅速发展的背景下&#xff0c;新质生产力的概念正在成为…

策略模式原理与C++实现

定义 定义一些列算法&#xff0c;把他们一个个封装起来&#xff0c;并且使他们可以相互替换&#xff08;变化&#xff09;。该模式使得算法可独立于使用它的客户程序&#xff08;稳定&#xff09;而变化&#xff08;扩展、子类化&#xff09;。 C实现 在不考虑策略模式的情况…

数学基础【俗说矩阵】:齐次线性方程和非齐次线性方程求解-学习笔记

一、矩阵基础知识 二元一次方程的传统解法 不论是代入消元法还是加减消元法都统称 【高斯消元法】。 齐次方程组和非齐次方程组 线性方程组的解 线性方程的向量展示 向量规则 矩阵的高斯消元和初等行变行及其规则 高斯消元规则 初等行变换 矩阵经初等行变换成阶梯矩阵&…

入坑树莓派(2)——树莓派4B与手机蓝牙通信

入坑树莓派(2)——树莓派4B与手机蓝牙通信 1、引言 在入坑树莓派(1)中已经搞掂了可视化问题。现在继续开展下一步,尝试与手机通信,一开始是想弄wifi连接的,但发现基于wifi的APP比较难弄,为了降低开发的难度,又因为树莓派板子自带蓝牙模块,所以直接选用蓝牙连接手机…

SAP 银企直连--FBPM生成付款文件增强

需求描述&#xff1a; SAP中通过运行事务码FBPM生成付款文件&#xff0c;触发传输将文件写入跳板机&#xff0c;写入跳板机之前&#xff0c;检查文件是否已存在&#xff0c;存在则给出提示无需执行 实现步骤 &#xff1a; 文件校验 参照标准函数FI_PAYMEDIUM_SAMPLE_21 创建自…

Vision Pro的增强视觉:企业级Unity插件包实现主摄像头访问

在AR和VR技术的快速发展中,Unity作为跨平台游戏和应用开发的首选引擎,其插件生态的丰富性一直是开发者们关注的焦点。最近,一个专为Vision Pro设计的Unity插件包——EnterpriseCameraAccessPlugin,因其能够通过企业API访问主摄像头的功能,引起了广泛关注。 一、插件背景与…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(二)-支持高分辨率视频直播应用

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…