Cesium淹没分析(动态纹理水体)

需求:生成好看的动态水纹理,并结合地形实现水动态淹没效果
实现思路
1.生成水纹理:通过着色器根据海洋参数,噪声参数,扩散反射来获得合适的水纹效果。
2. 结合地形实现水动态淹没效果:将生成的水纹理应用于水面材质。然后,根据地形的高度信息,实现水的淹没效果。通过调整透明度来实现水的淹没效果。
3. 实现水动态效果:为了让水看起来更真实,添加一些动态效果,使用法线贴图来模拟水面的波动,或者使用屏幕空间反射等技术来实现水面的反射效果。
实现效果
请添加图片描述
参数设定
自定义着色器纹理,绘制区域范围,开始高度(地形最低点),蔓延速度,蔓延最大高度
实现代码(基于cesium1.114版本实现):
1.初始化cesium

			Cesium.Ion.defaultAccessToken ="您自己的tk";
			Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(90, -20, 110, 90);
			var viewer = new Cesium.Viewer("cesiumContainer", {
				animation: false, //是否显示动画控件
				homeButton: false, //是否显示home键
				geocoder: false, //是否显示地名查找控件        如果设置为true,则无法查询
				baseLayerPicker: true, //是否显示图层选择控件
				timeline: true, //是否显示时间线控件
				fullscreenButton: false, //是否全屏显示
				scene3DOnly: true, //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
				infoBox: false, //是否显示点击要素之后显示的信息
				sceneModePicker: false, //是否显示投影方式控件  三维/二维
				navigationInstructionsInitiallyVisible: false,
				navigationHelpButton: false, //是否显示帮助信息控件
				selectionIndicator: false, //是否显示指示器组件
				shouldAnimate: true, //加上这条可以显示粒子(火焰,喷泉)
				contextOptions: { //屏幕截图必写
					webgl: {
						alpha: true,
						depth: true,
						stencil: true,
						antialias: true,
						premultipliedAlpha: true,
						preserveDrawingBuffer: true,
						failIfMajorPerformanceCaveat: true
					},
					allowTextureFilterAnisotropic: true
				},
				imageryProviderViewModels: [],
				imageryProvider: false
			});
			//忽略默认地图
			var imageryProviderViewModels = viewer.baseLayerPicker.viewModel.imageryProviderViewModels;
			viewer.baseLayerPicker.viewModel.selectedImagery = imageryProviderViewModels[imageryProviderViewModels.length - 1];
			//取消版权信息
			viewer._cesiumWidget._creditContainer.style.display = "none"
			//设置地球颜色
			viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString('#000d2d');
			viewer.scene.globe.enableLighting = false; //光线
			viewer.scene.globe.showGroundAtmosphere = true; //天气特效
			viewer.scene.fxaa = false; //抗锯齿,建议关闭
			viewer.scene.fog.enabled = false; //雾特效
			viewer.scene.globe.depthTestAgainstTerrain = false; //开启地形深度检测
			viewer.scene.debugShowFramesPerSecond = false; // 显示帧率
			let scene = window.viewer.scene;
			var token = '天地图token';
			// 服务域名
			var tdtUrl = 'https://t{s}.tianditu.gov.cn/';
			// 服务负载子域
			var subdomains = ['0', '1', '2', '3', '4', '5', '6', '7'];
			var img_w = viewer.imageryLayers.addImageryProvider(
				new Cesium.UrlTemplateImageryProvider({
					url: tdtUrl + 'DataServer?T=img_w&x={x}&y={y}&l={z}&tk=' + token,
					subdomains: subdomains,
					tilingScheme: new Cesium.WebMercatorTilingScheme(),
					maximumLevel: 18
				})
			);
			img_w.show = true;
			//初始位置设置
			viewer.camera.setView({
				destination: Cesium.Cartesian3.fromDegrees(123.80411007, 31.6286, 12002180.3), //经度,纬度,视角高
			});
			// const terrain = new Cesium.Terrain(Cesium.CesiumTerrainProvider.fromUrl("http://data.marsgis.cn/terrain/"));
			const terrain = new Cesium.Terrain(Cesium.CesiumTerrainProvider.fromUrl("自已的地形数据路径,也可以把上边的公用地形放开【但有时候会连接不上】"));
			viewer.scene.setTerrain(terrain);

2.绘制淹没区域

			let activeShapePoints = []
			let floatingPoint = undefined
			let activeShape = undefined
			let handler = undefined
			let isDraw = false;
			let maxWaterHeight = 2000;
			let minWaterHeight = 0;
			let waterHeight = 0;
			let waterPrimitive = undefined;
			let tempEntities = [];
			$("#huizhi").on('click', function() {
				activeShapePoints = []
				floatingPoint = undefined
				activeShape = undefined
				handler = undefinedisDraw = false;
				maxWaterHeight = 2000;
				minWaterHeight = 0;
				waterHeight = 0;
				waterPrimitive = undefined;
				tempEntities = [];
				// 开启深度检测
				viewer.scene.globe.depthTestAgainstTerrain = true
				handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
				handler.setInputAction((event) => {
					const earthPosition = viewer.scene.pickPosition(event.position);
					if (Cesium.defined(earthPosition)) {
						if (activeShapePoints.length === 0) {
							floatingPoint = createPoint(earthPosition);
							activeShapePoints.push(earthPosition);
							const dynamicPositions = new Cesium.CallbackProperty(function() {
								return new Cesium.PolygonHierarchy(activeShapePoints);
							}, false);
							activeShape = drawShape(dynamicPositions, Cesium.Color.fromBytes(64, 157, 253, 50));
						}
						activeShapePoints.push(earthPosition);
						tempEntities.push(createPoint(earthPosition))
					}
				}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
				handler.setInputAction((event) => {
					if (Cesium.defined(floatingPoint)) {
						const newPosition = viewer.scene.pickPosition(event.endPosition);
						if (Cesium.defined(newPosition)) {
							floatingPoint.position.setValue(newPosition);
							activeShapePoints.pop();
							activeShapePoints.push(newPosition);
						}
					}
				}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

				handler.setInputAction((event) => {
					activeShapePoints.pop()
					if (activeShapePoints.length < 3) return
					tempEntities.push(drawPolyline(activeShapePoints))
					let ploy = drawShape(activeShapePoints, Cesium.Color.fromBytes(64, 157, 253, 20))
					tempEntities.push(ploy)
					window.viewer.entities.remove(floatingPoint);
					window.viewer.entities.remove(activeShape);
					floatingPoint = undefined;
					activeShape = undefined;
					handler.destroy() // 关闭事件句柄
					handler = null
					induationAnalysis()
				}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
			});
			/**
			 * @author: 
			 * @Date: 2024-09-06 16:48:43
			 * @note: 注意事项
			 * @description: 获取区域内最大最小高程
			 * @param {*} positions
			 */
			const getAreaHeight = async (positions) => {
				let startP = positions[0]
				let endP = positions[positions.length - 1]
				if (startP.x != endP.x && startP.y != endP.y && startP.z != endP.z) positions.push(positions[0])

				const tempPoints = []
				for (let i = 0; i < positions.length; i++) {
					var ellipsoid = window.viewer.scene.globe.ellipsoid
					var cartographic = ellipsoid.cartesianToCartographic(positions[i])
					var lat = Cesium.Math.toDegrees(cartographic.latitude)
					var lng = Cesium.Math.toDegrees(cartographic.longitude)
					tempPoints.push([lng, lat])
				}
				var line = turf.lineString(tempPoints)
				var chunk = turf.lineChunk(line, 10, {
					units: 'meters'
				});
				const tempArray = []
				chunk.features.forEach(f => {
					f.geometry.coordinates.forEach(c => {
						tempArray.push(Cesium.Cartographic.fromDegrees(c[0], c[1]))
					})
				})
				var promise = Cesium.sampleTerrainMostDetailed(window.viewer.terrainProvider, tempArray)
				const updatedPositions = await Cesium.sampleTerrainMostDetailed(window.viewer.terrainProvider, tempArray) // 计算取样点的高度
				let minHeight = 8888
				let maxHeight = 0
				// 计算取样点的最小高度和最大高度
				for (let i = 0; i < updatedPositions.length; i++) {
					const height = updatedPositions[i].height
					console.log(height)
					if (height < minHeight) {
						minHeight = height
					}
					if (height > maxHeight) {
						maxHeight = height
					}
				}
				waterHeight = Math.ceil(minHeight)
				minWaterHeight = Math.ceil(minHeight)
				maxWaterHeight = Math.ceil(maxHeight)
				// 禁用绘制按钮
				isDraw = !isDraw
				return {
					minHeight,
					maxHeight
				}
			}
			/**
			 * @author: 
			 * @Date: 2024-09-06 16:46:47
			 * @note: 注意事项
			 * @description: 创建点
			 * @param {*} worldPosition
			 */
			function createPoint(worldPosition) {
				const point = window.viewer.entities.add({
					position: worldPosition,
					point: {
						color: Cesium.Color.SKYBLUE,
						pixelSize: 5,
						heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
					},
				});
				return point;
			}
			/**
			 * @author: 
			 * @Date: 2024-09-06 16:46:47
			 * @note: 注意事项
			 * @description: 绘制多边形
			 * @param {*} positionData
			 * @param {*} mat
			 */
			function drawShape(positionData, mat) {
				let shape = window.viewer.entities.add({
					polygon: {
						hierarchy: positionData,
						material: mat,
						outline: true,
						outlineColor: Cesium.Color.SKYBLUE,
						outlineWidth: 4,
					}
				});
				return shape;
			}
			/**
			 * @author: 
			 * @Date: 2024-09-06 16:46:11
			 * @note: 注意事项
			 * @description: 绘制线
			 * @param {*} positions
			 */
			function drawPolyline(positions) {
				if (positions.length < 1) return

				let startP = positions[0]
				let endP = positions[positions.length - 1]
				if (startP.x != endP.x && startP.y != endP.y && startP.z != endP.z) positions.push(positions[0])

				return window.viewer.entities.add({
					name: 'polyline',
					polyline: {
						positions: positions,
						width: 2.0,
						material: Cesium.Color.SKYBLUE,
						clampToGround: true
					}
				})
			}

3.淹没分析渲染

/**
			 * @author: 
			 * @Date: 2024-09-06 16:45:05
			 * @note: 注意事项
			 * @description: 淹没分析
			 */
			var induationAnalysis = async () => {
				await getAreaHeight(activeShapePoints)
				let newData = [];
				let positionsArr = [];
				for (let i = 0; i < activeShapePoints.length; i++) {
					if (i < activeShapePoints.length - 1) {
						let cartographic = Cesium.Cartographic.fromCartesian(activeShapePoints[i]);
						newData.push(parseFloat(Cesium.Math.toDegrees(cartographic.longitude).toFixed(6)), parseFloat(Cesium.Math.toDegrees(cartographic.latitude).toFixed(6)), 0)
						if(i == 0)
						{
							positionsArr.push(new Cesium.Cartesian2(0, 1))
						}else
						{
							if(i % 3 == 1)
							{
									positionsArr.push(new Cesium.Cartesian2(0, 0))
							}else if(i % 3 ==2)
							{
							 positionsArr.push(new Cesium.Cartesian2(1, 0))
							}else if(i % 3 ==0)
							{
								positionsArr.push(new Cesium.Cartesian2(1, 1))
							}
						}
					}
				}
			let textureCoordinates = {
					positions: positionsArr,
				};
				console.log(newData)
				let aper = new Cesium.MaterialAppearance({
					material: '找网上大佬们好看的水面动态纹理,也可留言!!!',
				});
				//加载实体
				let primitive = new WaterPrimitive({
					aper: aper, //着色器
					positions: newData, //范围
					height: Number(minWaterHeight), //开始高度
					textureCoordinates: textureCoordinates, 
					speedVal: 0.05, //速度
					extrudedHeight: Number(minWaterHeight),
					targetHeight: Number(maxWaterHeight), //最大高度
					flag:true   //true 涨水  false 降水
				})
				let primitArr = viewer.scene.primitives.add(primitive);
				tempEntities.push(primitArr);
				function renderLoop(timestamp){
				        aper.material.uniforms.iTime = timestamp/1000;
				        requestAnimationFrame(renderLoop);
				 }
				 renderLoop();
			}
			/**
			 * @author: 
			 * @Date: 2024-09-06 16:44:42
			 * @note: 注意事项
			 * @description: 清除当前页面所有数据
			 */
			function clearAllEntities() {
				positions = []
				const length = tempEntities.length
				for (let f = 0; f < length; f++) {
					window.viewer.scene.primitives.remove(tempEntities[f])
				}
				tempEntities = []
				activeShapePoints = [];
				warningWaterHeight = 0
				isDraw = !isDraw
				floatingPoint = undefined
				activeShape = undefined
				if (handler) {
					handler.destroy() // 关闭事件句柄
					handler = undefined
				}
			}

最终效果

cesium河流淹没效果(自定义动态水纹理)


完整代码链接:
添加链接描述
小弟不经常写文章写的不是很完美,有不足之处有多多指教。如需完整代码,请留言!!!

骋天淹没分析系统 骋天淹没分析系统是以三维地理信息系统为基础平台,基于数字高程模型(DEM)格网模型,通过改进迭代种子蔓延算法将淹没分析结果直观在三维地理系统系统上呈现出来。 骋天淹没分析系统应用于水库的库区淹没分析时,设置好起止水位和终止水位,以三维的形式呈现库区淹没区域,根据不同是水深来计算库容量,移民数量、直接经济影响和间接经济影响。可将数据制作成柱状图、饼状图、曲线图等多种多样的统计图;能够根据业务流程和用户要求定制各类表格,进行业务报表输出;还能按某一要素生成范围图、点密度图、分级符号图等,进行专题图分析,形象直观地反映防洪要素的时空变化规律。 骋天淹没分析系统亦可应用于洪水淹没分析时,根据洪水演进过程,配合数字化地图,利用三维模型,计算洪水淹没范围和淹没水深,并动态显示淹没区域并动态显示淹没区域,计算人口、家庭财产、工商、企业、农业、林业、渔业和畜牧业等淹没信息。并可根据预报调度、实时调度和历史调度等不同洪水下泻过程,计算分析洪水淹没损失,显示淹没分布状况,从而得出最佳洪水调度预案,提供泄洪区域内人员撤退、避灾转移和救灾物资供应等最佳行动路线。 广西骋天信息科技有限公司 网站 www.gxchengtian.com
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

坏孩纸 zxjys!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值