diff --git a/src/assets/icons/toolbar_fog.png b/src/assets/icons/toolbar_fog.png new file mode 100644 index 0000000..695d546 Binary files /dev/null and b/src/assets/icons/toolbar_fog.png differ diff --git a/src/assets/icons/toolbar_rain.png b/src/assets/icons/toolbar_rain.png new file mode 100644 index 0000000..0068e57 Binary files /dev/null and b/src/assets/icons/toolbar_rain.png differ diff --git a/src/assets/icons/toolbar_snow.png b/src/assets/icons/toolbar_snow.png new file mode 100644 index 0000000..aa17782 Binary files /dev/null and b/src/assets/icons/toolbar_snow.png differ diff --git a/src/assets/icons/toolbar_sunny.png b/src/assets/icons/toolbar_sunny.png new file mode 100644 index 0000000..b05f08b Binary files /dev/null and b/src/assets/icons/toolbar_sunny.png differ diff --git a/src/components/CesiumMap/mixins/useWeatherParticle/fog.js b/src/components/CesiumMap/mixins/useWeatherParticle/fog.js new file mode 100644 index 0000000..9caf265 --- /dev/null +++ b/src/components/CesiumMap/mixins/useWeatherParticle/fog.js @@ -0,0 +1,74 @@ +/* + * @Descripttion: 雾粒子 + * @Author: 笙痞 + * @Date: 2023-01-04 18:11:32 + * @LastEditors: 笙痞77 + * @LastEditTime: 2023-05-19 09:56:16 + */ +import * as Cesium from "cesium"; +class FogEffect { + constructor(viewer, options) { + if (!viewer) throw new Error("no viewer object!"); + options = options || {}; + this.visibility = Cesium.defaultValue(options.visibility, 0.1); // 能见度 + this.color = Cesium.defaultValue( + options.color, + new Cesium.Color(0.8, 0.8, 0.8, 0.5) + ); + this.viewer = viewer; + this.init(); + } + + init() { + this.fogStage = new Cesium.PostProcessStage({ + name: "czm_fog", + fragmentShader: this.fog(), + uniforms: { + visibility: () => { + return this.visibility; + }, + fogColor: () => { + return this.color; + }, + }, + }); + this.viewer.scene.postProcessStages.add(this.fogStage); + } + + destroy() { + if (!this.viewer || !this.fogStage) return; + this.viewer.scene.postProcessStages.remove(this.fogStage); + const isDestroyed = this.fogStage.isDestroyed(); + // 先检查是否被销毁过,如果已经被销毁过再调用destroy会报错 + if (!isDestroyed) { + this.fogStage.destroy(); + } + delete this.visibility; + delete this.color; + } + + show(visible) { + this.fogStage.enabled = visible; + } + + fog() { + return "uniform sampler2D colorTexture;\n\ + uniform sampler2D depthTexture;\n\ + uniform float visibility;\n\ + uniform vec4 fogColor;\n\ + in vec2 v_textureCoordinates; \n\ + out vec4 fragColor;\n\ + void main(void) \n\ + { \n\ + vec4 origcolor = texture(colorTexture, v_textureCoordinates); \n\ + float depth = czm_readDepth(depthTexture, v_textureCoordinates); \n\ + vec4 depthcolor = texture(depthTexture, v_textureCoordinates); \n\ + float f = visibility * (depthcolor.r - 0.3) / 0.2; \n\ + if (f < 0.0) f = 0.0; \n\ + else if (f > 1.0) f = 1.0; \n\ + fragColor = mix(origcolor, fogColor, f); \n\ + }\n"; + } +} + +export default FogEffect; \ No newline at end of file diff --git a/src/components/CesiumMap/mixins/useWeatherParticle/index.js b/src/components/CesiumMap/mixins/useWeatherParticle/index.js new file mode 100644 index 0000000..c7141cc --- /dev/null +++ b/src/components/CesiumMap/mixins/useWeatherParticle/index.js @@ -0,0 +1,54 @@ +import * as Cesium from "cesium"; +import RainEffect from "./rain"; +import SnowEffect from "./snow"; +import FogEffect from "./fog"; + +export const useWeatherParticle = (viewer) => { + const _viewer = viewer; + let weather = null + + const destroy = () => { + if(weather) { + weather.destroy(); + weather = null; + } + }; + + const rain = () => { + destroy(); + + weather = new RainEffect(_viewer, { + tiltAngle: -0.2, //倾斜角度 + rainSize: 1.0, // 雨大小 + rainSpeed: 120.0, // 雨速 + }); + weather.show(true); + }; + + const snow = () => { + destroy(); + + weather = new SnowEffect(_viewer, { + snowSize: 0.02, // ❄️大小,最好小于0.02 + snowSpeed: 60.0, // 速度 + }); + weather.show(true); + }; + + const fog = () => { + destroy(); + + weather = new FogEffect(_viewer, { + visibility: 0.2, // 能见度 + color: new Cesium.Color(0.8, 0.8, 0.8, 0.3), + }); + weather.show(true); + }; + + return { + rain, + snow, + fog, + destroy, + } +}; diff --git a/src/components/CesiumMap/mixins/useWeatherParticle/rain.js b/src/components/CesiumMap/mixins/useWeatherParticle/rain.js new file mode 100644 index 0000000..39e8ebc --- /dev/null +++ b/src/components/CesiumMap/mixins/useWeatherParticle/rain.js @@ -0,0 +1,86 @@ +/* + * @Descripttion: 雨粒子效果 + * @Author: 笙痞 + * @Date: 2023-01-04 15:01:03 + * @LastEditors: 笙痞77 + * @LastEditTime: 2023-05-19 09:54:15 + */ +import * as Cesium from "cesium"; + +class RainEffect { + constructor(viewer, options) { + if (!viewer) throw new Error("no viewer object!"); + options = options || {}; + //倾斜角度,负数向右,正数向左 + this.tiltAngle = Cesium.defaultValue(options.tiltAngle, -0.6); + this.rainSize = Cesium.defaultValue(options.rainSize, 0.3); + this.rainSpeed = Cesium.defaultValue(options.rainSpeed, 60.0); + this.viewer = viewer; + this.init(); + } + + init() { + this.rainStage = new Cesium.PostProcessStage({ + name: "czm_rain", + fragmentShader: this.rain(), + uniforms: { + tiltAngle: () => { + return this.tiltAngle; + }, + rainSize: () => { + return this.rainSize; + }, + rainSpeed: () => { + return this.rainSpeed; + }, + }, + }); + this.viewer.scene.postProcessStages.add(this.rainStage); + } + + destroy() { + if (!this.viewer || !this.rainStage) return; + this.viewer.scene.postProcessStages.remove(this.rainStage); + const isDestroyed = this.rainStage.isDestroyed(); + // 先检查是否被销毁过,如果已经被销毁过再调用destroy会报错 + if (!isDestroyed) { + this.rainStage.destroy(); + } + delete this.tiltAngle; + delete this.rainSize; + delete this.rainSpeed; + } + + show(visible) { + this.rainStage.enabled = visible; + } + + rain() { + return "uniform sampler2D colorTexture;\n\ + in vec2 v_textureCoordinates;\n\ + uniform float tiltAngle;\n\ + uniform float rainSize;\n\ + uniform float rainSpeed;\n\ + float hash(float x) {\n\ + return fract(sin(x * 133.3) * 13.13);\n\ + }\n\ + out vec4 fragColor;\n\ + void main(void) {\n\ + float time = czm_frameNumber / rainSpeed;\n\ + vec2 resolution = czm_viewport.zw;\n\ + vec2 uv = (gl_FragCoord.xy * 2. - resolution.xy) / min(resolution.x, resolution.y);\n\ + vec3 c = vec3(.6, .7, .8);\n\ + float a = tiltAngle;\n\ + float si = sin(a), co = cos(a);\n\ + uv *= mat2(co, -si, si, co);\n\ + uv *= length(uv + vec2(0, 4.9)) * rainSize + 1.;\n\ + float v = 1. - sin(hash(floor(uv.x * 100.)) * 2.);\n\ + float b = clamp(abs(sin(20. * time * v + uv.y * (5. / (2. + v)))) - .95, 0., 1.) * 20.;\n\ + c *= v * b;\n\ + fragColor = mix(texture(colorTexture, v_textureCoordinates), vec4(c, 1), .5);\n\ + }\n\ + "; + } +} + +export default RainEffect; \ No newline at end of file diff --git a/src/components/CesiumMap/mixins/useWeatherParticle/snow.js b/src/components/CesiumMap/mixins/useWeatherParticle/snow.js new file mode 100644 index 0000000..36b9ffc --- /dev/null +++ b/src/components/CesiumMap/mixins/useWeatherParticle/snow.js @@ -0,0 +1,86 @@ +/* + * @Descripttion: 雪花粒子类 + * @Author: 笙痞 + * @Date: 2023-01-04 14:01:07 + * @LastEditors: 笙痞77 + * @LastEditTime: 2023-05-19 09:52:52 + */ + +import * as Cesium from "cesium"; +class SnowEffect { + constructor(viewer, options) { + if (!viewer) throw new Error("no viewer object!"); + options = options || {}; + this.snowSize = Cesium.defaultValue(options.snowSize, 0.02); // ❄️大小,最好小于0.02 + this.snowSpeed = Cesium.defaultValue(options.snowSpeed, 60.0); // 速度 + this.viewer = viewer; + this.init(); + } + + init() { + this.snowStage = new Cesium.PostProcessStage({ + name: "czm_snow", + fragmentShader: this.snow(), + uniforms: { + snowSize: () => { + return this.snowSize; + }, + snowSpeed: () => { + return this.snowSpeed; + }, + }, + }); + this.viewer.scene.postProcessStages.add(this.snowStage); + } + + destroy() { + if (!this.viewer || !this.snowStage) return; + this.viewer.scene.postProcessStages.remove(this.snowStage); + const isDestroyed = this.snowStage.isDestroyed(); + // 先检查是否被销毁过,如果已经被销毁过再调用destroy会报错 + if (!isDestroyed) { + this.snowStage.destroy(); + } + delete this.snowSize; + delete this.snowSpeed; + } + show(visible) { + this.snowStage.enabled = visible; + } + + snow() { + return "uniform sampler2D colorTexture;\n\ + in vec2 v_textureCoordinates;\n\ + uniform float snowSpeed;\n\ + uniform float snowSize;\n\ + float snow(vec2 uv,float scale)\n\ + {\n\ + float time=czm_frameNumber/snowSpeed;\n\ + float w=smoothstep(1.,0.,-uv.y*(scale/10.));if(w<.1)return 0.;\n\ + uv+=time/scale;uv.y+=time*2./scale;uv.x+=sin(uv.y+time*.5)/scale;\n\ + uv*=scale;vec2 s=floor(uv),f=fract(uv),p;float k=3.,d;\n\ + p=.5+.35*sin(11.*fract(sin((s+p+scale)*mat2(7,3,6,5))*5.))-f;d=length(p);k=min(d,k);\n\ + k=smoothstep(0.,k,sin(f.x+f.y)*snowSize);\n\ + return k*w;\n\ + }\n\ + out vec4 fragColor;\n\ + void main(void){\n\ + vec2 resolution=czm_viewport.zw;\n\ + vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);\n\ + vec3 finalColor=vec3(0);\n\ + //float c=smoothstep(1.,0.3,clamp(uv.y*.3+.8,0.,.75));\n\ + float c=0.;\n\ + c+=snow(uv,30.)*.0;\n\ + c+=snow(uv,20.)*.0;\n\ + c+=snow(uv,15.)*.0;\n\ + c+=snow(uv,10.);\n\ + c+=snow(uv,8.);\n\ + c+=snow(uv,6.);\n\ + c+=snow(uv,5.);\n\ + finalColor=(vec3(c));\n\ + fragColor=mix(texture(colorTexture,v_textureCoordinates),vec4(finalColor,1),.5);\n\ + }\n\ + "; + } +} +export default SnowEffect; \ No newline at end of file diff --git a/src/views/systemTemplate/forestFire/Toolbar/Toolbar.vue b/src/views/systemTemplate/forestFire/Toolbar/Toolbar.vue index f0d3433..019c695 100644 --- a/src/views/systemTemplate/forestFire/Toolbar/Toolbar.vue +++ b/src/views/systemTemplate/forestFire/Toolbar/Toolbar.vue @@ -46,6 +46,7 @@ import * as Cesium from 'cesium'; import { useDrawTool } from '@/components/CesiumMap/mixins/useDrawTool'; import { useEventBus } from '@/components/CesiumMap/mixins/useEventBus'; import { useMeasureTool } from '@/components/CesiumMap/mixins/useMeasureTool'; +import { useWeatherParticle } from '@/components/CesiumMap/mixins/useWeatherParticle'; import { drawLocation, drawPolyline, @@ -100,6 +101,11 @@ import toolbarWarehouseIcon from '@/assets/icons/toolbar_warehouse.png'; import toolbarWatersourceIcon from '@/assets/icons/toolbar_watersource.png'; import toolbarRoadIcon from '@/assets/icons/toolbar_road.png'; import toolbarKeyareaIcon from '@/assets/icons/toolbar_keyarea.png'; +// 气象效果图标 +import toolbarSunnyIcon from '@/assets/icons/toolbar_sunny.png'; +import toolbarRainIcon from '@/assets/icons/toolbar_rain.png'; +import toolbarSnowIcon from '@/assets/icons/toolbar_snow.png'; +import toolbarFogIcon from '@/assets/icons/toolbar_fog.png'; const props = defineProps({ viewer: { @@ -111,6 +117,7 @@ const props = defineProps({ let viewer = null let drawTool = null let measureTool = null +let weatherParticle = null let bus = null let toolbarLayer = null let primitiveList = [] @@ -242,6 +249,32 @@ const options = ref([ id: 3, icon: weatherIcon, label: '气象效果', + subtools: [ + { + id: 31, + name: 'sunny', + label: '晴天', + icon: toolbarSunnyIcon + }, + { + id: 32, + name: 'rain', + label: '下雨', + icon: toolbarRainIcon + }, + { + id: 33, + name: 'snow', + label: '下雪', + icon: toolbarSnowIcon + }, + { + id: 34, + name: 'fog', + label: '起雾', + icon: toolbarFogIcon + }, + ] }, { id: 4, @@ -288,7 +321,7 @@ watch(() => props.viewer, (v) => { drawTool = useDrawTool(viewer); measureTool = useMeasureTool(viewer); - // 绑定事件 + // 绘制、测量事件参数 params = { drawTool, measureTool, @@ -296,6 +329,11 @@ watch(() => props.viewer, (v) => { toolbarLayer, primitiveList, } + + // 气象效果 + weatherParticle = useWeatherParticle(viewer); + + // 绑定事件 bus = useEventBus(viewer); // 基础绘制部分 bus.on('toolbar_location', drawLocation); @@ -320,6 +358,11 @@ watch(() => props.viewer, (v) => { bus.on('toolbar_warehouse', drawWarehouse); bus.on('toolbar_road', drawRoad); bus.on('toolbar_keyarea', drawKeyarea); + // 气象效果部分 + bus.on('toolbar_sunny', () => weatherParticle && weatherParticle.destroy()) + bus.on('toolbar_rain', () => weatherParticle && weatherParticle.rain()) + bus.on('toolbar_snow', () => weatherParticle && weatherParticle.snow()) + bus.on('toolbar_fog', () => weatherParticle && weatherParticle.fog()) // 清空 bus.on('toolbar_clear', toolbarClear); }