水面效果
參考:
http://cesiumcn.org/topic/158.html
http://api.rivermap.cn/cesium/rivermap/map.html
https://blog.csdn.net/weixin_42496466/article/details/80747565
demo效果:
主要代碼:
//繪制水面波浪效果
drawWater: function(){
this.viewer.scene.globe.depthTestAgainstTerrain = false;
var waterFace=[
130.0, 30.0, 0,
150.0, 30.0, 0,
150.0, 10.0, 0,
130.0, 10.0, 0];
var waterPrimitive = new Cesium.Primitive({
show:true,// 默認隱藏
allowPicking:false,
geometryInstances : new Cesium.GeometryInstance({
geometry : new Cesium.PolygonGeometry({
polygonHierarchy : new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArrayHeights(waterFace)),
//extrudedHeight: 0,//注釋掉此屬性可以只顯示水面
//perPositionHeight : true//注釋掉此屬性水面就貼地了
})
}),
// 可以設置內置的水面shader
appearance : new Cesium.EllipsoidSurfaceAppearance({
material : new Cesium.Material({
fabric : {
type : 'Water',
uniforms : {
//baseWaterColor:new Cesium.Color(0.0, 0.0, 1.0, 0.5),
//blendColor: new Cesium.Color(0.0, 0.0, 1.0, 0.5),
//specularMap: 'gray.jpg',
//normalMap: '../assets/waterNormals.jpg',
normalMap: '本地貼圖地址 或 base64',
frequency: 1000.0,
animationSpeed: 0.01,
amplitude: 10.0
}
}
}),
fragmentShaderSource:'varying vec3 v_positionMC;\nvarying vec3 v_positionEC;\nvarying vec2 v_st;\nvoid main()\n{\nczm_materialInput materialInput;\nvec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));\n#ifdef FACE_FORWARD\nnormalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n#endif\nmaterialInput.s = v_st.s;\nmaterialInput.st = v_st;\nmaterialInput.str = vec3(v_st, 0.0);\nmaterialInput.normalEC = normalEC;\nmaterialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);\nvec3 positionToEyeEC = -v_positionEC;\nmaterialInput.positionToEyeEC = positionToEyeEC;\nczm_material material = czm_getMaterial(materialInput);\n#ifdef FLAT\ngl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n#else\ngl_FragColor = czm_phong(normalize(positionToEyeEC), material);\
gl_FragColor.a=0.5;\n#endif\n}\n'//重寫shader,修改水面的透明度
})
});
this.viewer.scene.primitives.add(waterPrimitive);
this.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(140, 20, 6000000.0),
orientation : {
heading: Cesium.Math.toRadians(0.0), //默認朝北0度,順時針方向,東是90度
pitch: Cesium.Math.toRadians(-90), //默認朝下看-90,0為水平看,
roll: Cesium.Math.toRadians(0) //默認0
}
});
}
注意這里
this.viewer.scene.globe.depthTestAgainstTerrain = false;
而淹沒效果需要將其設置為 true;當其值為 true 時,水面效果 會出現縫隙,如下圖所示。
貼圖從參考鏈接中可獲取,這里附上:
http://api.rivermap.cn/cesium/Build/CesiumUnminified/Assets/Textures/waterNormals.jpg
淹沒效果
參考:
https://github.com/liyangis/sn_cesium
demo效果:
主要代碼(包含未用到的熱力圖效果代碼):
import Cesium from 'cesium/Source/Cesium' import HeatMap from "../modules/heatmap"; // 淹沒分析 export default class SubmergenceAnalysis { constructor(viewer, isTerrain = true, height_max, height_min, step, map_type,positionsArr,speed) { this.viewer = viewer this.isTerrain = isTerrain this.handler = null this.tempEntities = [] this.polygonEntities = [] this.linePositionList = [] this.tempPoints = [] this.extrudedHeight = height_min this.height_max = height_max this.height_min = height_min this.step = step // 默認是范圍圖/深度圖 this.map_type = map_type this.polygon_degrees = positionsArr this.speed = speed //this._initViewStatus(this.viewer) this._addDisListener() } _initViewStatus(viewer) { var scene = viewer.scene scene.globe.depthTestAgainstTerrain = true viewer.camera.flyTo({ //scene.camera.setView({ // 攝像頭的位置 destination: Cesium.Cartesian3.fromDegrees(108.9, 34, 5000.0), orientation: { heading: Cesium.Math.toRadians(0.0),//默認朝北0度,順時針方向,東是90度 pitch: Cesium.Math.toRadians(-20),//默認朝下看-90,0為水平看, roll: Cesium.Math.toRadians(0)//默認0 } }); viewer.skyAtmosphere = false } // 根據矩形范圍得到行列數點坐標和高程信息 _getPoints(xmin, xmax, ymin, ymax) { const x_count = 10 const y_count = 10 let cartesians = new Array(x_count * y_count); const x_d = (xmax - xmin) / x_count for (var i = 0; i < x_count; ++i) { const start_pt = { x: xmin + i * x_d, y: ymax } const end_pt = { x: xmin + i * x_d, y: ymin } for (let j = 0; j < y_count; j++) { const offset = j / (y_count - 1); const x = Cesium.Math.lerp(start_pt.x, end_pt.x, offset); const y = Cesium.Math.lerp(start_pt.y, end_pt.y, offset); cartesians[j + i * y_count] = Cesium.Cartographic.fromDegrees(x, y); } } return cartesians } _getHeights(cartesians, extrudedHeight, callback) { var terrainProvider = new Cesium.createWorldTerrain({ requestVertexNormals: true }) // 根據地形計算某經緯度點的高度 var promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartesians); Cesium.when(promise, function (updatedPositions) { let positions = updatedPositions.filter(d => { const cartographic = d if (cartographic) { const h_d = extrudedHeight - cartographic.height return h_d > 0 } }) positions = positions.map(d => { const cartographic = d let h = extrudedHeight - cartographic.height return { x: Cesium.Math.toDegrees(cartographic.longitude), y: Cesium.Math.toDegrees(cartographic.latitude), value: h } }) if (callback) { callback(positions) } }); } _addDisListener() { let viewer = this.viewer let scene = viewer.scene let linePositionList = this.linePositionList viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK) this.handler = new Cesium.ScreenSpaceEventHandler(scene.canvas) // 繪制線 this._drawLine(linePositionList) //this.loadGrandCanyon() // 繪制面 if (this.map_type) { this._drawPoly(this.polygon_degrees) } else { // 得到插值網格 const bounds = { west: 115.8784, east: 115.9614, south: 39.9912, north: 40.0381 } const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north) this._getHeights(positions_cartesian, this.extrudedHeight, (d) => { this.heatMapObj = new HeatMap(this.viewer, d, bounds); }) } } _reDraw() { this.tempPoints = [] this.linePositionList.length = 0 this.areaPositionList.length = 0 for (let entity of this.tempEntities) { this.viewer.entities.remove(entity) } this.tempEntities = [] } _drawLine(linePositionList) { let lineStyle = { width: 2, material: Cesium.Color.CHARTREUSE } let entity = this.viewer.entities.add({ polyline: lineStyle, }) entity.polyline.positions = new Cesium.CallbackProperty(function () { return linePositionList }, false) this.polygonEntities.push(entity) } _drawPoint(point_Cartesian3) { let entity = this.viewer.entities.add({ position: point_Cartesian3, point: { pixelSize: 10, color: Cesium.Color.GOLD, // disableDepthTestDistance: Number.POSITIVE_INFINITY, // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND } }) this.tempEntities.push(entity) } _drawPoly(degrees) { const that = this let entity = this.viewer.entities.add({ polygon: { hierarchy: {}, material: new Cesium.Color.fromBytes(64, 157, 253, 100), perPositionHeight: true, } }) entity.polygon.hierarchy = new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(degrees)) entity.polygon.extrudedHeight = new Cesium.CallbackProperty(() => that.extrudedHeight, false) this.polygonEntities.push(entity) } // 世界坐標轉經緯坐標 _car3ToLatLon(cartesian) { let cartographic = Cesium.Cartographic.fromCartesian(cartesian) let longitudeString = Cesium.Math.toDegrees(cartographic.longitude) let latitudeString = Cesium.Math.toDegrees(cartographic.latitude) return { lon: longitudeString, lat: latitudeString, height: cartographic.height } } //移除整個資源 remove() { let viewer = this.viewer for (let tempEntity of this.tempEntities) { viewer.entities.remove(tempEntity) } for (let lineEntity of this.polygonEntities) { viewer.entities.remove(lineEntity) } this.handler.destroy() } start() { const that = this this.timer = window.setInterval(() => { if ((that.height_max > that.extrudedHeight) && (that.extrudedHeight >= that.height_min)) { that.extrudedHeight = that.extrudedHeight + that.step } else { that.extrudedHeight = that.height_min } if (!that.map_type) { if (this.heatMapObj) { const bounds = { west: 115.8784, east: 115.9614, south: 39.9912, north: 40.0381 } const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north) this._getHeights(positions_cartesian, this.extrudedHeight, (d) => { this.heatMapObj.update(d); }) } } },that.speed*1000) if (that.map_type) { that._drawPoly(that.polygon_degrees) } else { if (this.heatMapObj) { } } } clear() { let viewer = this.viewer if (this.timer) { window.clearInterval(this.timer) this.timer = null } this.extrudedHeight = this.height_min; if (this.heatMapObj) this.heatMapObj.show(false) for (let entity of this.polygonEntities) { viewer.entities.remove(entity) } viewer.skyAtmosphere = true; } changeMapType(type) { if (!type) { if (!this.heatMapObj) { // 得到插值網格 const bounds = { west: 115.8784, east: 115.9614, south: 39.9912, north: 40.0381 } const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north) this._getHeights(positions_cartesian, this.extrudedHeight, (d) => { this.heatMapObj = new HeatMap(this.viewer, d, bounds); }) } this.heatMapObj && this.heatMapObj.show(true) for (let entity of this.polygonEntities) { entity.show = false; } } else { this.heatMapObj.show(false) for (let entity of this.polygonEntities) { entity.show = true; } } } // 切割一部分地形 loadGrandCanyon() { var globe = this.viewer.scene.globe; const viewer = this.viewer // viewer.skyAtmosphere = false, // Pick a position at the Grand Canyon var position = Cesium.Cartographic.toCartesian(new Cesium.Cartographic.fromDegrees(115.9165534, 40.0139345, 100)); var distance = 30000.0; var boundingSphere = new Cesium.BoundingSphere(position, distance); globe.clippingPlanes = new Cesium.ClippingPlaneCollection({ modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(position), planes: [ new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 0.0, 0.0), distance), new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), distance), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), distance), new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, -1.0, 0.0), distance) ], unionClippingRegions: true }); globe.clippingPlanes.enabled = true; viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.5, -0.5, boundingSphere.radius * 5.0)); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); } }
洪水淹沒效果
在上一步的基礎上,希望達到“洪水順着河流向前淹沒”效果
效果如下圖:(注:只能在特定地區,特定觀察角度,效果才會稍好一點 😅)
原理:沿河道下游方向連續繪制長條狀的藍色立方體(由長條狀矩形polygon拉伸而來),並使用上文“淹沒效果”不斷抬升。
幾點需要注意:
1、上下游落差較大,因此上下游的立方體,起始高度不同(demo里僅僅做了均勻減少一定值的操作,實際應根據該位置河道具體高度)
2、長條狀立方體的南北寬度要超過河道(demo里設置為固定值,實際應根據該位置河道具體寬度設置)
3、因為是連續繪制長條狀立方體,所以相鄰立方體之間的連接處很明顯,效果較差。
4、目前只是演示,挑了一段東西走向的河道,繪制方向僅僅是向東每次平移一個長條立方體寬度的距離,不符合實際情況。
這個demo演示了連續(根據長條狀矩形polygon)繪制立方體的效果,只能作為演示。
實際應根據dem 處理獲得很多個連續的polygon(非長條狀,包含時間屬性、高度屬性),然后逐一繪制。
dem的數據處理參考:
https://blog.csdn.net/wqy248/article/details/81119550
待下一步測試。~