cesium 水面、淹沒 效果


水面效果

參考:

 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

待下一步測試。~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM