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

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏: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);
}

<think>首先,用户的问题是:“我想用户想知道是否可以在原生Cesium中使用Vue-Cesium组件 请问原生 Cesium 是否兼容 vue-cesium 组件”。这似乎是关于Cesium和Vue-Cesium的兼容性。 从系统级指令中,我需要: - 所有行内数学表达式用$...$格式。 - 独立公式用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用:引用[1], [2], [3], [4]。 站内引用内容: - 引用[1]: 关于Cesium和Vue-Cesium的介绍。 - 引用[2]: 关于在Vue中集成原生Cesium的配置。 - 引用[3]: Vue + Cesium + Ant Design Vue Pro的配置教程。 - 引用[4]: Vue2 + Cesium 1.89.0使用cesium-navigation-es6的教程。 用户的问题是:原生Cesium是否兼容vue-cesium组件?意思是,能否在原生Cesium项目中使用Vue-Cesium组件。 关键点: - 原生Cesium:指纯JavaScript的Cesium库。 - Vue-Cesium:是一个Vue组件库,用于在Vue应用中封装Cesium功能。 在原生Cesium中直接使用Vue-Cesium组件可能不合适,因为Vue-Cesium是为Vue框架设计的。它封装了Cesium功能,使其易于在Vue组件中使用。 从引用中: - 引用[1]提到Vue for Cesium是一个框架,帮助简化开发。 - 引用[2]和[3]讨论了如何在Vue项目中集成原生Cesium- 引用[4]是关于在Vue中使用Cesium插件。 所以,原生Cesium是纯JS库,而Vue-Cesium是一个Vue组件库。它们不是直接兼容的;Vue-Cesium依赖于Vue框架。 回答结构: 1. 澄清概念:解释原生Cesium和Vue-Cesium是什么。 2. 兼容性问题:直接说明是否兼容。 3. 替代方案:建议如何在原生Cesium中使用类似功能,或如何在Vue项目中集成。 4. 引用相关引用以支持观点。 5. 生成相关问题。 最终回答: - 使用中文。 - 结构清晰。 - 添加引用标识。 - 生成相关问题。 逐步回答: 1. 介绍原生Cesium和Vue-Cesium。 2. 解释兼容性:原生Cesium是独立的JS库,Vue-Cesium是为Vue设计的组件,因此不能在原生Cesium中直接使用Vue-Cesium组件。 3. 建议:如果要在原生Cesium中使用,可能需要手动集成或使用其他方法;如果使用Vue,可以集成原生Cesium或直接使用Vue-Cesium。 4. 引用相关部分:例如,引用[2]和[3]展示了如何在Vue中配置原生Cesium。 5. 生成相关问题。 相关问题生成: - 基于主题:Cesium、Vue、兼容性。 - 例如: 1. 如何在原生Cesium项目中集成Vue组件? 2. Vue-Cesium的主要优势是什么? 3. 在Vue中使用原生Cesium的步骤是什么? 4. 有哪些替代方案来简化Cesium开发? 确保回答真实可靠:基于常识和提供的引用。</think>### 原生 Cesium 是否兼容 vue-cesium 组件? 原生 Cesium 是一个独立的 JavaScript 库,专注于高性能 3D 地理空间可视化,它不依赖于任何前端框架(如 Vue)[^1]。而 vue-cesium 是一个专门为 Vue.js 框架设计的组件库,它封装了 Cesium功能,提供声明式组件以简化开发。因此,**原生 Cesium 无法直接兼容 vue-cesium 组件**,原因如下: 1. **架构差异**: - 原生 Cesium 是纯 JavaScript API,需要通过 DOM 操作和事件监听手动集成到页面中。 - vue-cesium 是基于 Vue 的封装,依赖 Vue 的响应式系统和组件生命周期。在原生 Cesium 环境中,Vue 的虚拟 DOM 和组件机制无法直接运行[^1]。 2. **依赖关系**: - vue-cesium 要求项目基于 Vue.js 框架(如 Vue 2 或 Vue 3),而原生 Cesium 项目通常是纯 HTML/JS 应用。如果强行在原生 Cesium 中引入 vue-cesium 组件,会导致依赖冲突(如 Vue 未加载)或渲染错误[^2][^3]。 3. **替代方案**: - **在 Vue 项目中集成原生 Cesium**:如果您希望结合 Vue 的便利性和 Cesium功能,可以参考引用[2][^2]和[3][^3]的方法,通过 Webpack 配置(如 `DefinePlugin`)和全局注册,将原生 Cesium 嵌入 Vue 应用。例如: ```javascript // main.js 配置(Vue 项目) import Cesium from 'cesium/Cesium'; import 'cesium/Widgets/widgets.css'; Vue.prototype.Cesium = Cesium; // 全局挂载 ``` 这样可在 Vue 组件中使用原生 Cesium API,而无需 vue-cesium- **使用 vue-cesium 在 Vue 项目中**:如果目标是简化开发,建议直接采用 vue-cesium 库,它提供预封装组件(如地图视图、相机控制),减少学习曲线[^1]。 总之,原生 Cesium 和 vue-cesium 设计目标不同,无法直接互用。选择方案取决于项目框架:纯 JS 项目用原生 Cesium;Vue 项目可选用原生集成或 vue-cesium 以提升效率[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值