cesium-天气效果
cesium可以实现天气变化的效果,官方是使用cesium粒子系统做的效果。
但这种实现方式有问题:当视角变化(拉近、拉远、平移、旋转)时,粒子就会变化,甚至会消失。
我这里通过 Shader 这种方式模拟雨雪,可以不受视点位置的影响,相当于是一个全屏的后处理。
下雪天气(snow)
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);
this.snowStage.destroy();
delete this.snowSize;
delete this.snowSpeed;
}
show(visible) {
this.snowStage.enabled = visible;
}
snow() {
return "uniform sampler2D colorTexture;\n\
varying 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\
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\
gl_FragColor=mix(texture2D(colorTexture,v_textureCoordinates),vec4(finalColor,1),.5);\n\
}\n\
";
}
}
Cesium.SnowEffect = SnowEffect;
class RainEffect {
constructor(viewer, options) {
if (!viewer) throw new Error('no viewer object!');
options = options || {};
//倾斜角度,负数向右,正数向左
this.tiltAngle = Cesium.defaultValue(options.tiltAngle, -.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);
this.rainStage.destroy();
delete this.tiltAngle;
delete this.rainSize;
delete this.rainSpeed;
}
show(visible) {
this.rainStage.enabled = visible;
}
rain() {
return "uniform sampler2D colorTexture;\n\
varying 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\
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\
gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(c, 1), .5);\n\
}\n\
";
}
}
Cesium.RainEffect = RainEffect;
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._show = Cesium.defaultValue(options.show, !0);
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);
this.fogStage.destroy();
delete this.visibility;
delete this.color;
}
show(visible) {
this._show = visible;
this.fogState.enabled = this._show;
}
fog() {
return "uniform sampler2D colorTexture;\n\
uniform sampler2D depthTexture;\n\
uniform float visibility;\n\
uniform vec4 fogColor;\n\
varying vec2 v_textureCoordinates; \n\
void main(void) \n\
{ \n\
vec4 origcolor = texture2D(colorTexture, v_textureCoordinates); \n\
float depth = czm_readDepth(depthTexture, v_textureCoordinates); \n\
vec4 depthcolor = texture2D(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\
gl_FragColor = mix(origcolor, fogColor, f); \n\
}\n";
}
}
Cesium.FogEffect = FogEffect;
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Use correct character set. -->
<meta charset="utf-8"/>
<!-- Tell IE to use the latest, best version. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<title>cesium-天气效果</title>
<script src="../lib/Cesium-1.89/Build/Cesium/Cesium.js"></script>
<script src="../../static/lib/vue.min.js"></script>
<script src="../../static/lib/snow.js"></script>
<script src="../../static/lib/rain.js"></script>
<script src="../../static/lib/fog.js"></script>
<style>
@import url(../lib/Cesium-1.89/Build/Cesium/Widgets/widgets.css);
html,
body, #temp {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="temp">
<div style="display: -webkit-flex;display: flex;width: 100%;height: 100%">
<div style="width: 90%;height: 100%">
<div id="cesiumContainer"></div>
</div>
<div style="width: 10%;height: 100%;background-color: #d3d3d3;padding: 30px">
<button class="btn" @click="addSnow">下雪</button>
<button class="btn" @click="addRain">下雨</button>
<button class="btn" @click="addFog">下雾</button>
<button class="btn" @click="destoryWeather">清除</button>
</div>
</div>
</div>
<script>
let EarthComp = new Vue({
el: "#temp",
data: {
_viewer: undefined,
snow:null,//雪
rain:null,//雨
fog:null,//雾
},
mounted: function () {
let that = this;
this.earthInit();
},
methods: {
/**
* 地球初始化
*/
earthInit() {
//天地图token
let TDT_tk = "token";
//Cesium token
let cesium_tk = "token";
//标注
let TDT_CIA_C = "http://{s}.tianditu.gov.cn/cia_c/wmts?service=wmts&request=GetTile&version=1.0.0" +
"&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}" +
"&style=default&format=tiles&tk=" + TDT_tk;
// 添加mapbox自定义地图实例
let layer = new Cesium.MapboxStyleImageryProvider({
url: 'https://api.mapbox.com/styles/v1',
username: 'sungang',
styleId: 'styleId',
accessToken: 'accessToken',
scaleFactor: true
});
//初始页面加载
Cesium.Ion.defaultAccessToken = cesium_tk;
let viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false, // 位置查找工具
baseLayerPicker: false,// 图层选择器(地形影像服务)
timeline: false, // 底部时间线
homeButton: false,// 视角返回初始位置
fullscreenButton: false, // 全屏
animation: false, // 左下角仪表盘(动画器件)
sceneModePicker: false,// 选择视角的模式(球体、平铺、斜视平铺)
navigationHelpButton: false, //导航帮助按钮
imageryProvider: layer
});
//调用影响中文注记服务
viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: TDT_CIA_C,
layer: "tdtImg_c",
style: "default",
format: "tiles",
tileMatrixSetID: "c",
subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
tilingScheme: new Cesium.GeographicTilingScheme(),
tileMatrixLabels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"],
maximumLevel: 50,
show: false
}))
this._viewer = viewer;
// 去除版权信息
this._viewer._cesiumWidget._creditContainer.style.display = "none";
// 初始化dem位置
this.addDem();
},
/**
* 添加地形图
*/
addDem() {
let that = this;
let terrainProvider = new Cesium.CesiumTerrainProvider({
url: '../res/data/dem/ASTGTM_N29E087D'
});
that._viewer.terrainProvider = terrainProvider;
that._viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(87.54791, 29.57025, 17863.0),
orientation: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-25.0),
roll: 0.0
}
});
},
/**
* 下雪
*/
addSnow() {
let that = this;
that.destoryWeather();
that.snow = new Cesium.SnowEffect(that._viewer, {
snowSize: 0.02, //雪大小 ,默认可不写
snowSpeed: 60.0 //雪速,默认可不写
});
},
/**
* 下雨
*/
addRain() {
let that = this;
that.destoryWeather();
that.rain = new Cesium.RainEffect(that._viewer, {
tiltAngle: -.6, //倾斜角度
rainSize: 0.6, //雨大小
rainSpeed: 120.0 //雨速
});
},
/**
* 下雾
*/
addFog() {
let that = this;
that.destoryWeather();
that.fog = new Cesium.FogEffect(that._viewer, {
visibility: 0.2,
color: new Cesium.Color(0.8, 0.8, 0.8, 0.3)
});
},
/**
* 消除天气效果
*/
destoryWeather() {
if (this.snow) {
this.snow.destroy();
}
if (this.rain) {
this.rain.destroy();
}
if (this.fog) {
this.fog.destroy();
}
},
},
})
</script>
</body>
</html>