在 Cesium 开发中,我们经常需要对加载的 3D Tileset 模型进行材质的自定义修改。本文将介绍如何使用 CustomShader 来替换模型的默认材质,并实现一个简单的动态呼吸效果。
该方案不依赖模型的具体几何特征(如法线方向),而是通过全局的材质重写,确保在不同类型的模型上都能呈现一致的效果。
需求分析
我们的目标是修改模型的材质表现:
- 修改颜色:统一使用青色(Cyan)基调。
- 动态效果:材质亮度随时间进行律动(呼吸效果)。
- 兼容性:不依赖模型坐标系原点或法线方向,避免不同数据源导致的效果差异。
- LOD 优化:解决远距离模型消失的问题。
核心代码实现
我们使用 CustomShader 的 PBR(基于物理的渲染)模式,并利用 Cesium 内置变量实现动画。
Shader 代码
const roofShader = new cesium.CustomShader({
// 1. 设置光照模式:PBR 模式
lightingModel: cesium.LightingModel.PBR,
// 2. 定义 Uniforms:传递参数
uniforms: {
u_roofColor: {
value: new cesium.Cartesian3(0.0, 1.0, 1.0),
type: cesium.UniformType.VEC3
}
},
// 3. Fragment Shader:核心逻辑
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
// 定义基础色调
vec3 baseColor = vec3(0.0, 1.0, 1.0);
// --- 动态效果:呼吸 ---
// 使用内置变量 czm_frameNumber (每帧+1) 替代 u_time,无需 CPU 每帧更新 Uniform
// 系数推导:0.1 * 60FPS = 6.0,2π/6 ≈ 1秒,对应约 60 BPM 的频率
float breathe = 0.6 + 0.4 * sin(czm_frameNumber * 0.1);
// --- 材质重写 ---
// 1. 漫反射 (Diffuse):降低亮度
material.diffuse = baseColor * 0.2;
// 2. 自发光 (Emissive):应用呼吸效果
material.emissive = baseColor * breathe;
// 3. 高光与粗糙度 (Specular & Roughness)
material.specular = vec3(1.0);
material.roughness = 0.3;
}
`
});
技术要点
使用 czm_frameNumber 实现动画
实现动画通常需要传入 u_time。Cesium 内置了 czm_frameNumber 全局 Uniform。虽然它代表帧数而非绝对时间,但在帧率相对稳定的场景下,直接使用它乘以一个系数来驱动 sin 函数,既能获得平滑的动画,又省去了 JavaScript 与 WebGL 之间的通信开销。
解决 LOD 导致的模型消失
应用 Shader 后,可能会遇到**“相机拉远后模型突然消失”**的问题。这是 Cesium 的 SSE (Screen Space Error) 优化机制导致的。
需要在加载 Tileset 时覆盖以下配置:
const tilesetOptions = {
// ...应用我们的 Shader
customShader: roofShader,
// 关键配置 1:最大屏幕空间误差设为 16(默认可能为 64)
// 强制 Cesium 在远距离也加载并渲染模型,而不是将其剔除
maximumScreenSpaceError: 16,
// 关键配置 2:禁止动态 SSE 调整
// 防止在帧率波动时,Cesium 自动降低画质导致远处的建筑闪烁或消失
dynamicScreenSpaceError: false
};
// 加载模型
const tileset = await cesium.Cesium3DTileset.fromUrl(url, tilesetOptions);
// 双重保险:显式赋值(解决部分 Tileset 初始化时配置未生效的偶发问题)
tileset.customShader = roofShader;
mapViewer.scene.primitives.add(tileset);
总结
通过 CustomShader,我们以较低的成本实现了自定义的材质效果。
- 设计上:简化逻辑,不依赖纹理和坐标判断。
- 性能上:利用内置变量和 PBR 属性,计算成本低。
- 工程上:通过合理的 LOD 设置,保证了模型在不同距离下的可见性。