就这?你模拟的这个水可太小儿科了!

1,797 阅读3分钟

大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端提效、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第22/100篇文章。
交流合作:brown_7778。

前言

上次发了一篇水淹模拟的动画演示cesium中如何高性能渲染3D模型(附水淹分析模拟)

其中的水就是简单的用entity渲染了一个立方体并附着了颜色。

说句实话,这效果如果摆在甲方面前,他可能上来就会给我一个大逼斗!

所以我痛定思痛,决定认真努力工作,立马端正态度。

思路

要想让水体变得逼真,除了要用一个几何体去充当它的载体,更重要的是要有真实的水体材质,把材质铺贴在几何体之上。

但是光有材质也不行,因为水是动态的,所以还要想办法让材质动起来。

绘制几何体

先用RectangleGeometry画一个矩形的几何体

const rectangleGeometry = new Cesium.RectangleGeometry({
  // fromDegrees接收西、南、东、北四个经纬线的具体坐标,从而框出一个矩形
  rectangle: Cesium.Rectangle.fromDegrees(120.34, 36.06, 120.42, 36.13),
  // 要计算的顶点属性
  vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
  // 几何体的垂直高度
  height: 1000,
})

可能有的小伙伴不知道Cesium.Rectangle.fromDegrees参数的四个点要怎么获取,具体描述如下:

再画一张意象图:

有的小伙伴说,我理解了这个范围取值,但是我要怎么在地球上去获取具体的坐标点位呢?这也难不倒我们,我们可以直接在cesium初始化成功之后,开发一个辅助获取坐标点位的功能。

获取点击坐标

// 监听点击事件,拾取具体坐标点位
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((e) => {
  const clickPosition = viewer.scene.camera.pickEllipsoid(e.position);
  const randiansPos = Cesium.Cartographic.fromCartesian(clickPosition);
  console.log(
    "经度:" +
      Cesium.Math.toDegrees(randiansPos.longitude) +
      ", 纬度:" +
      Cesium.Math.toDegrees(randiansPos.latitude)
  );
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

以上代码就是通过监听鼠标左键点击,获取到屏幕坐标,再把屏幕坐标转换成投影坐标系

创建几何体实例

const geometryInstance = new Cesium.GeometryInstance({
  geometry: rectangleGeometry
})

Primitive

创建图元几何图形,渲染几何体,并给几何体上材质。

const primitive = new Cesium.Primitive({
  geometryInstances: geometryInstance,
  // appearance用于渲染图元的外观
  appearance: new Cesium.EllipsoidSurfaceAppearance({
    // 材质参数
    material: new Cesium.Material({
      fabric: {
        type: "Water",
        uniforms: {
          baseWaterColor: new Cesium.Color(
            64 / 255.0,
            157 / 255.0,
            253 / 255.0,
            0.5
          ),
          normalMap: "/images/waterNormals.jpg",
          frequency: 1000.0,
          animationSpeed: 0.1,
          amplitude: 10,
          specularIntensity: 10,
        },
      },
    }),
  })
})
  • fabric:通过配置cesium提供的现有的一些参数组合,可以幕后生成并组装成 glsl 着色器代码

具体参数组合参考:cesium.xin/cesium/cn/D…

换句话说:cesium已经提前预置好了一些着色器代码,并可以通过fabric这个参数的配置去生成一些常见的效果,而省去我们需要写glsl代码的一些成本。

所以我们直接选择可以生成的代码片段,参数解析如下:

  • baseWaterColor : rgba 颜色对象水的基色。

  • blendColor :从水混合到非水区域时使用的 rgba 颜色对象。

  • specularMap :用于指示水域的单通道纹理。

  • normalMap :水法线扰动的法线贴图。

  • frequency :控制波数的数字。

  • animationSpeed : 控制水的动画速度的数字。

  • amplitude :控制水波振幅的数字。

  • specularIntensity :控制镜面反射强度的数字。

以上,然后对应我们的代码,我们就可以通过调整这些参数配置,去调整水的样式了,非常方便。

最后别忘记,把生成的primitive添加到场景当中。

viewer.scene.primitives.add(primitive)

就这样,一个真实的水体就通过cesium成功的渲染出来了。

7月7日.gif

甲方看了后直竖大拇指:这就叫专业!

【开源地址】:github.com/tingyuxuan2…

有需要进技术产品开发交流群(可视化&GIS)可以加我:brown_7778,也欢迎数字孪生可视化领域的交流合作。

最后,如果觉得文章对你有帮助,也希望可以一键三连👏👏👏,支持我持续开源和分享~