Cesium 應用1- SceneWeather添加雨雪天氣場景

最近項目中要實現一個根據天氣狀況自動添加雨雪天氣場景的功能,先看官網demo,發現已經有示例Particle System Weaher

cesium中顯示雪的效果時雪的顆粒度比較大,而且切換視角後就有些問題了

網上查過,有其他解決方案,果斷放棄這種實現方法,具體如下:

其他解決方案網址:網址

思路:利用postProcessStages接口實現全屏效果,利用postProcessStages在片元着色器中拿到v_textureCoordinates,然後自己添加雨雪效果,相當於在鏡頭前添加粒子效果,與場景位置視角無關,解決了上述的問題。

實現方法:

方法一:

和博客介紹一致是將shader代碼放在Source/Shaders/PostProcessStages文件夾下,修改ceisum源碼中的PostProcessStageLibray類,將雨雪效果內嵌到cesium源碼中,這個需要自己重新編譯ceisum,修改源碼,

方法二:直接利用PostProcessStage對象實現動態創建,不需要修改源碼

如API所述,可修改fragmentShader屬性,修改着色器即可,示例和代碼如下

snow.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="utf-8" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1">
    <title>Tools-29postProcess-Snow</title>
    <script src="1-2ImageryProvider-WebExtend.js"></script>  -->
	<script src="https://cesiumjs.org/releases/1.50/Build/Cesium/Cesium.js"></script>
	<link href="https://cesiumjs.org/releases/1.50/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
	<script src="./js/1-2ImageryProvider-WebExtend.js"></script> 
    <style>
    
        html, body, #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="creditContainer" style="display: none;"></div>
   
    <script>
        var GoogleMap = ImageryProviderWebExtendTool.createGoogleMapsByUrl(Cesium, { url: "http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}" });
        var viewer = new Cesium.Viewer('cesiumContainer', {
            imageryProvider: GoogleMap,
            contextOptions: {
                webgl: {
                    alpha: true,
                    depth: false,
                    stencil: true,
                    antialias: true,
                    premultipliedAlpha: true,
                    preserveDrawingBuffer: true,  
                    failIfMajorPerformanceCaveat: true
                },
                allowTextureFilterAnisotropic: true
            },
            creditContainer: "creditContainer",
            selectionIndicator: false,
            animation: false,   
            baseLayerPicker: false,  
            geocoder: false,  
            timeline: false,  
            sceneModePicker: true,  
            navigationHelpButton: false,  
            infoBox: false,   
            fullscreenButton: true
        });
        
        var lat = 42.006;
        var lon = 128.055;
        //取消雙擊事件
        viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
        //設置homebutton的位置
        Cesium.Camera.DEFAULT_VIEW_RECTANGLE =
                Cesium.Rectangle.fromDegrees(lon - 1, lat - 1, lon + 1, lat + 1);//Rectangle(west, south, east, north)
        //設置初始位置
        viewer.camera.setView({
            destination: Cesium.Cartesian3.fromDegrees(lon, lat, 300000)
        });
        //定義下雪場景 着色器
        function FS_Snow() {
            return "uniform sampler2D colorTexture;\n\
    varying vec2 v_textureCoordinates;\n\
\n\
    float snow(vec2 uv,float scale)\n\
    {\n\
        float time = czm_frameNumber / 60.0;\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)*0.01);\n\
        return k*w;\n\
    }\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 = 0.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), 0.5); \n\
\n\
    }\n\
";
        }
        var collection = viewer.scene.postProcessStages;
        var fs_snow = FS_Snow();
        var snow = new Cesium.PostProcessStage({
            name: 'czm_snow',
            fragmentShader: fs_snow
        });
        collection.add(snow);
        viewer.scene.skyAtmosphere.hueShift = -0.8;
        viewer.scene.skyAtmosphere.saturationShift = -0.7;
        viewer.scene.skyAtmosphere.brightnessShift = -0.33;
        viewer.scene.fog.density = 0.001;
        viewer.scene.fog.minimumBrightness = 0.8;
    </script>
</body>
</html>

其中1-2ImageryProvider-WebExtend.js如下:

var ImageryProviderWebExtendTool = (
    function () {
        function _() { }

        _.createGoogleMapsByUrl = function (Cesium, options) {
            options = Cesium.defaultValue(options, {});

            var templateUrl = Cesium.defaultValue(options.url, 'http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}');

            var trailingSlashRegex = /\/$/;
            var defaultCredit = new Cesium.Credit('Google Maps');

            var tilingScheme = new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid });

            var tileWidth = 256;
            var tileHeight = 256;

            var minimumLevel = Cesium.defaultValue(options.minimumLevel, 0);
            var maximumLevel = Cesium.defaultValue(options.minimumLevel, 17);

            var rectangle = Cesium.defaultValue(options.rectangle, tilingScheme.rectangle);

            // Check the number of tiles at the minimum level.  If it's more than four,
            // throw an exception, because starting at the higher minimum
            // level will cause too many tiles to be downloaded and rendered.
            var swTile = tilingScheme.positionToTileXY(Cesium.Rectangle.southwest(rectangle), minimumLevel);
            var neTile = tilingScheme.positionToTileXY(Cesium.Rectangle.northeast(rectangle), minimumLevel);
            var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1);
            //>>includeStart('debug', pragmas.debug);
            if (tileCount > 4) {
                throw new Cesium.DeveloperError('The rectangle and minimumLevel indicate that there are ' + tileCount + ' tiles at the minimum level. Imagery providers with more than four tiles at the minimum level are not supported.');
            }
            //>>includeEnd('debug');

            var credit = Cesium.defaultValue(options.credit, defaultCredit);
            if (typeof credit === 'string') {
                credit = new Cesium.Credit(credit);
            }

            return new Cesium.UrlTemplateImageryProvider({
                url: templateUrl,
                proxy: options.proxy,
                credit: credit,
                tilingScheme: tilingScheme,
                tileWidth: tileWidth,
                tileHeight: tileHeight,
                minimumLevel: minimumLevel,
                maximumLevel: maximumLevel,
                rectangle: rectangle
            });
        }
        return _;
    }
)();

下雨的例子:

<!DOCTYPE html>
<html>
	<head lang="en">
		<meta charset="utf-8" />
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="initial-scale=1, maximum-scale=1">
		<title>Tools-29postProcess-Snow</title>
		<!-- <script src="../../Cesium1.48/Cesium/Cesium.js"></script>-->
		<script src="https://cesiumjs.org/releases/1.50/Build/Cesium/Cesium.js"></script>
		<link href="https://cesiumjs.org/releases/1.50/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
		<script src="./js/1-2ImageryProvider-WebExtend.js"></script>
		<style>
			/*  @import url(../../Cesium1.48/Cesium/Widgets/widgets.css); */
			html,
			body,
			#cesiumContainer {
				width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
				overflow: hidden;
			}
		</style>
	</head>
	<body>
		<div id="cesiumContainer" class="fullSize"></div>
		<div id="creditContainer" style="display: none;"></div>

		<script>
			var GoogleMap = ImageryProviderWebExtendTool.createGoogleMapsByUrl(Cesium, {
				url: "http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}"
			});
			var viewer = new Cesium.Viewer('cesiumContainer', {
				imageryProvider: GoogleMap,
				contextOptions: {
					webgl: {
						alpha: true,
						depth: false,
						stencil: true,
						antialias: true,
						premultipliedAlpha: true,
						preserveDrawingBuffer: true,
						failIfMajorPerformanceCaveat: true
					},
					allowTextureFilterAnisotropic: true
				},
				creditContainer: "creditContainer",
				selectionIndicator: false,
				animation: false,
				baseLayerPicker: false,
				geocoder: false,
				timeline: false,
				sceneModePicker: true,
				navigationHelpButton: false,
				infoBox: false,
				fullscreenButton: true
			});

			var lat = 42.006;
			var lon = 128.055;
			//取消雙擊事件
			viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
			//設置homebutton的位置
			Cesium.Camera.DEFAULT_VIEW_RECTANGLE =
				Cesium.Rectangle.fromDegrees(lon - 1, lat - 1, lon + 1, lat + 1); //Rectangle(west, south, east, north)
			//設置初始位置
			viewer.camera.setView({
				destination: Cesium.Cartesian3.fromDegrees(lon, lat, 300000)
			});
			//定義下雨場景 着色器
			function FS_Rain() {
				return "uniform sampler2D colorTexture;\n\
				varying vec2 v_textureCoordinates;\n\
			\n\
				float hash(float x){\n\
					return fract(sin(x*133.3)*13.13);\n\
			}\n\
			\n\
			void main(void){\n\
			\n\
				float time = czm_frameNumber / 60.0;\n\
			vec2 resolution = czm_viewport.zw;\n\
			\n\
			vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);\n\
			vec3 c=vec3(.6,.7,.8);\n\
			\n\
			float a=-.4;\n\
			float si=sin(a),co=cos(a);\n\
			uv*=mat2(co,-si,si,co);\n\
			uv*=length(uv+vec2(0,4.9))*.3+1.;\n\
			\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\
			\n\
			gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(c,1), 0.5);  \n\
			}\n\
";
			}
			var collection = viewer.scene.postProcessStages;
			var fs_rain = FS_Rain();
			var snow = new Cesium.PostProcessStage({
				name: 'czm_rain',
				fragmentShader: fs_rain
			});
			collection.add(snow);
			viewer.scene.skyAtmosphere.hueShift = -0.8;
			viewer.scene.skyAtmosphere.saturationShift = -0.7;
			viewer.scene.skyAtmosphere.brightnessShift = -0.33;
			viewer.scene.fog.density = 0.001;
			viewer.scene.fog.minimumBrightness = 0.8;
		</script>
	</body>
</html>

整合在一起的例子如下:

部分代碼:

移除雨雪方法:

function removeStage() {
lastStage && viewer.scene.postProcessStages.remove(lastStage), lastStage = null
}

添加雨方法

function showRain() {
	removeStage();
	var e = new Cesium.PostProcessStage({
			name: "czm_rain",
			fragmentShader: getRainShader()
	});
    viewer.scene.postProcessStages.add(e), lastStage = e
}

function getRainShader() {
return "uniform sampler2D colorTexture;\n    varying vec2 v_textureCoordinates;\n                    \n     float hash(float x){\n    return fract(sin(x*133.3)*13.13);\n    }\n                 \n     void main(void){\n    \n 
 float time = czm_frameNumber / 120.0;\n                    vec2 resolution = czm_viewport.zw;\n                    \n                    vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);\n                    vec3 c=vec3(.6,.7,.8);\n                    \n                    float a=-.4;\n                    float si=sin(a),co=cos(a);\n                    uv*=mat2(co,-si,si,co);\n                    uv*=length(uv+vec2(0,4.9))*.3+1.;\n                    \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                    \n                    gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(c,1), 0.5);  \n                    }\n                    "
}

添加雪方法

function showSnow() {
	removeStage();
	var e = new Cesium.PostProcessStage({
			name: "czm_snow",
			fragmentShader: getSnowShader()
	});
	viewer.scene.postProcessStages.add(e), lastStage = e
}
function getSnowShader() {
	return "uniform sampler2D colorTexture;\n  
 varying vec2 v_textureCoordinates;\n                    \n                     
   float snow(vec2 uv,float scale)\n  {\n                           
   float time = czm_frameNumber / 60.0;\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)*0.01);\n                       
     return k*w;\n                        }\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 = 0.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), 0.5); \n                    \n                        }\n                    "
}

注:自cesium1.46版本起纔有PostProcessStage接口,之前的版本沒有,如果想使用最好將cesium版本升級

 

關注

如果有問題,請在下方評論

想獲得更多的學習知識請關注微信公衆號:西北碼農或掃下方二維碼


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章