有机猴灬残暴Sama

# Cesium 全局特效(二)

# Cesium 全局特效:水淹效果(流动材质和动态水淹)

2024/01/01 15:41:20

# 说明

目前实现的全屏 Cesium 水淹场景渲染如下:

  • 流动水面材质:全球范围水淹和自定义区域水淹;
  • 动态淹没:自定义区域水淹

流动水面材质为什么不能动态实现淹没呢?原因很简单,电脑性能顶不住(每次变化都要重新清除后渲染,我顶配的电脑都顶不住啊),而且效果真的很一般(效果差我猜测是因为瓦片图不是一次性加载,优先加载的是顶层瓦片,而较为顶层瓦片的地形高度应该不准确,所以就算水淹全球,在地球都在窗口内时除了喜马拉雅山脉其他地方都是被淹没的效果,除非你拉到比较靠近地面,否则高度很不准确)

# 先上效果图

主要有两种,这里的图片用的是网上的,因为我不好去录制并转化gif,我下面的代码实现的效果会好一点 图片

图片1:UI 界面

图片

图片2:动态淹没

图片

图片3:流动水面

# 公共的实现代码

import type { Viewer } from 'cesium'
import {
    CallbackProperty, Cartesian3, Color, CylinderGeometry, EllipsoidSurfaceAppearance, GeometryInstance, Material,
    Primitive, Rectangle, RectangleGeometry, Transforms,
} from 'cesium'

const SCENE_SIM_OTHER: { [key: string]: any } = {
    FLOOD: { name: '水淹模拟', key: 'FLOOD' },
}

// 水淹参数接口
interface FloodIF {
    height: number
    isGlobe: boolean // 是否全球范围水淹
    animate: boolean // 是否采用动画形式
    center: Position
    radius: number
    targetHeight: number
    speed: number
}

/**
 * 淹没分析函数,通过拉伸面的高度来进行分析
 * @param {*} viewer
 * @param {*} targetWaterHeight :目标水位高度
 * @param {*} positions :研究区域底部坐标数组
 * @param {*} waterHeight :当前水位高度
 */
export function induationAnalysis(viewer: Viewer, flood: FloodIF) {
    // 如果要实时更新水位高度用entity,primitives无法动态更新
    if (flood.animate) {
        let height = flood.height
        // 颜色模拟的水面效果
        const heightCallback = new CallbackProperty(() => {
            height += flood.speed
            if (height > flood.targetHeight)
                height = flood.targetHeight

            return height
        }, false)

        let entity
        if (flood.isGlobe)
            entity = viewer.entities.add({
                id: SCENE_SIM_OTHER.FLOOD.key,
                name: SCENE_SIM_OTHER.FLOOD.key,
                rectangle: {
                    coordinates: Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0),
                    height: heightCallback,
                    material: Color.fromBytes(64, 157, 253, 150),
                },
            })

        else
            entity = viewer.entities.add({
                id: SCENE_SIM_OTHER.FLOOD.key,
                name: SCENE_SIM_OTHER.FLOOD.key,
                position: Cartesian3.fromDegrees(flood.center.lon, flood.center.lat),
                cylinder: {
                    length: heightCallback,
                    topRadius: flood.radius,
                    bottomRadius: flood.radius,
                    slices: 20, // 切片数,影响圆柱体的细节
                    material: Color.fromBytes(64, 157, 253, 150),
                },
            })

        return entity
    }
    else {
        let geometryInstances: GeometryInstance
        if (flood.isGlobe)
            geometryInstances = new GeometryInstance({
                geometry: new RectangleGeometry({
                    rectangle: Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0),
                    extrudedHeight: flood.height,
                    vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                }),
            })

        else
            geometryInstances = new GeometryInstance({
                geometry: new CylinderGeometry({
                    length: flood.height, // 高度
                    topRadius: flood.radius, // 顶部半径
                    bottomRadius: flood.radius, // 底部半径
                    slices: 20, // 切片数,影响圆柱体的细节
                    vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                }),
                modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(flood.center.lon, flood.center.lat)), // 设置圆柱体位置
            })

        // 流动水面效果
        const polygonPrimitive = viewer?.scene.primitives.add(
            new Primitive({
                geometryInstances,
                appearance: new EllipsoidSurfaceAppearance({
                    aboveGround: false,
                    material: new Material({
                        fabric: {
                            type: 'Water',
                            uniforms: {
                                baseWaterColor: Color.fromBytes(64, 157, 253, 150),
                                normalMap: 'cesium/waterNormals.jpg',
                                frequency: 1000.0,
                                animationSpeed: 0.1,
                                amplitude: 10,
                                specularIntensity: 10,
                            },
                        },
                    }),
                }),
            }),
        )

        return polygonPrimitive // 利用这个清除水淹效果
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

# 流动水淹材质来源于官网的贴图

图片

cesium 官网水贴图 waterNormals.jpg

# 清除水淹效果

// 清除流动水淹
viewer.scene.primitives.remove(polygonPrimitive)
// 清除动态水淹
viewer.entities.remove(entity)
1
2
3
4
Last Updated: 1/19/2024, 2:14:15 PM