Cesium实现积雪
在 Cesium 中实现逼真的积雪效果:基于法线的着色器技术
在三维可视化应用中,积雪效果是增强场景真实感和季节变化的重要元素。特别是在城市规划、景观设计和数字孪生等领域,逼真的积雪效果可以大大提升用户体验。本文将深入探讨如何使用基于法线的着色器技术在 Cesium 中实现高质量的积雪效果,并解析其背后的图形学原理。
在现实世界中,雪的积累有其特定规律:
- 顶面和向上表面积雪最多
- 垂直表面(如墙壁)积雪很少或没有
- 悬挑底面基本没有积雪
- 积雪在边缘和转角处有平滑过渡
实现逼真的积雪效果,关键在于模拟这些自然规律,而法线向量正是解决这个问题的理想工具。
基于法线的积雪原理
在 3D 图形学中,法线(normal)是垂直于表面的单位向量,它指明了表面的朝向。通过计算表面法线与雪的降落方向(通常是向下的向量)之间的关系,我们可以确定表面应该积累多少雪。
具体来说:
- 当表面法线与雪方向相同时(如水平屋顶),该表面会积累最多的雪
- 当表面法线与雪方向垂直时(如垂直墙面),该表面几乎不会积雪
- 当表面法线与雪方向相反时(如悬挑底面),该表面不会有雪
GLSL 着色器实现
下面是实现积雪效果的关键 GLSL 着色器代码:
1 | if(u_snow){ // 如果启用了雪效果 |
这段代码的核心是通过法线和雪方向的点积来计算积雪程度,然后使用混合函数将原始材质与雪的白色进行混合。
代码详解
让我们逐步分析这段代码的工作原理:
1. 坐标系转换
1 | vec3 normalEC = fsInput.attributes.normalEC; // 获取眼睛坐标系中的法线 |
在 Cesium 中,片元着色器通常接收的是眼睛坐标系(Eye Coordinates, EC)中的法线。但为了正确计算积雪效果,我们需要在模型坐标系(Model Coordinates, MC)中工作,因此需要进行坐标转换。czm_inverseNormal是 Cesium 提供的转换矩阵。
2. 点积计算
1 | float m = dot(normalMC, u_snowDirection); // 计算法线和雪方向的点积 |
点积操作dot(normalMC, u_snowDirection)是这个效果的核心,它计算了表面法线与雪方向之间的余弦值:
- 当两个向量方向一致时,点积为 1
- 当两个向量垂直时,点积为 0
- 当两个向量方向相反时,点积为-1
这正好符合我们对积雪分布的期望:朝上的表面积雪最多,垂直表面几乎没有,朝下的表面完全没有。
3. 非线性增强
1 | m = pow(m,5.0); // 对点积结果进行5次幂运算,使效果更加集中 |
点积的结果是线性的,但现实中的积雪分布并非线性。通过应用幂函数pow(m,5.0),我们可以非线性地增强效果:
- 接近 1 的值(如 0.9)经过 5 次幂后仍然很大(约 0.59)
- 中等的值(如 0.5)经过 5 次幂后变得很小(约 0.03)
- 小于 0 的值在 clamp 后会被截断为 0
这使得积雪效果更加集中在朝向雪方向的表面上,创造更逼真的视觉效果。
4. 颜色混合
1 | material.diffuse = mix(color, white, clamp(m,0.0,1.0) * u_snowAlpha); // 混合原始颜色和白色 |
最后,我们使用 GLSL 的mix函数将原始颜色与白色(代表雪)进行混合。混合系数由两部分组成:
clamp(m,0.0,1.0)确保混合因子在 0 到 1 之间u_snowAlpha控制整体雪的透明度,可以用来调整雪的厚度
另一种实现方式
以下是一个稍微不同的实现方式,同样基于法线计算,但有一些细微的差异:
1 | // 计算表面法线与方向向量(u_tilesetNormal)的点积 |
这个版本使用了稍浅一点的白色(0.8,0.8,0.8)而非纯白色,可能在某些场景下看起来更自然。
在 Cesium 中应用
要在 Cesium 中应用这个着色器效果,我们需要创建自定义着色器并应用到 3D Tileset 或模型上。以下是一个简化的实现示例:
1 | // 定义积雪着色器 |