在 Cesium 开发中,实体(Entity)的材质系统是实现个性化可视化效果的核心。Cesium 内置了丰富的材质类型,但面对复杂的业务需求(如动态流动的发光线、渐变纹理等),自定义 MaterialProperty 成为必然选择。

若要实现自定义材质属性(MaterialProperty),核心是继承并实现 Cesium 提供的抽象基类。
本文将基于 “发光流动线” 需求,完整拆解自定义 MaterialProperty 的实现逻辑。
一、核心前提:自定义 MaterialProperty 的底层要求
Cesium 的 MaterialProperty 是抽象类,需强制实现 2 个只读属性 和 3 个核心方法,同时要遵循其响应式机制(属性变更触发视图更新)和材质缓存注册规则。

我们参考cesium源码中的colorMaterialProperty,可以看到他有两个只读的属性和 三个方法:
分析这段代码并结合相关文档以及cesium运行机制我们发现定义一个实体材质需要注意以下方面
1、私有变量规范:在构造函数中,我们发现有带_的变量,这种变量属于材质属性中的私有变量,外部不要去直接访问与修改,而是通过不带下划线的变量,比如color,再去修改属性
2、响应式实现:Object.defineProperties(ColorMaterialProperty.prototype,...}中的内容,就是使用createPropertyDescriptor去对_变量做更新,更新之后的事件触发等。
3、必填属性约束:
- 定义isConstant属性:这个属性用来控制这个材质属性是否随clock的变化而变化
- 定义definitionChanged属性,这个属性是一个event,用来监听变化核心方法约束:需实现三个核心方法:
4、必填属性约束:
getType:返回材质唯一标识,用于匹配缓存中的材质;getValue:获取当前时间对应的材质属性值(传给着色器的 uniform 变量);equals:判断两个材质属性是否等价,用于 Cesium 识别材质是否变化;
5、材质注册:最终需将自定义材质注册到 Cesium 材质缓存,确保渲染时能被正确加载;在这个阶段,可以自定义着色器

二、搭建 PolylineFlowMaterialProperty 基础框架
第一步:搭建类的基础框架
先定义 PolylineFlowMaterialProperty 类,明确静态属性、构造函数、必填属性和方法的机构:
class PolylineFlowMaterialProperty {
// 静态材质类型名,用于材质缓存注册(全局唯一)
static materialType = "PolylineFlow";
constructor(options) {}
// 只读属性:是否为常量(不随时间变化)
get isConstant() {}
// 只读属性:属性变化事件
get definitionChanged() {}
// 获取材质类型名
getType(time) {}
// 获取指定时间的材质参数
getValue(time, result) {}
// 判断两个材质属性是否等价
equals(other) {}
}
三、准备流动线核心着色器(Shader)
着色器是材质渲染效果的核心,以下 shader 已实现 “发光流动 + 拖尾” 效果,需外部传入 3 个关键参数(后续会通过属性定义关联):
color:流动线基础颜色;glowPower:发光强度;taperPower:颜色 / 亮度递减强度;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
vec2 st = materialInput.st;
czm_material material = czm_getDefaultMaterial(materialInput);
// 动态时间参数,用于流动效果
float iTime = fract(czm_frameNumber / 60.0);
iTime = clamp(iTime, 0.0, 1.0);
// 颜色基础设置(需要外部传入 color)
vec3 baseColor = color.rgb;
// 拖尾参数设置(st.s方向)可以通过外界传入
float headLength = 0.05;
float tailLength = 0.1;
// 横向宽度控制(st.t方向),中心在0.5处
float verticalWidth = 0.5;
float glowFalloff = 1.0 - smoothstep(0.0, verticalWidth, abs(st.t - 0.5));
// 拖尾遮罩计算(平滑拖尾)
float headMask = smoothstep(iTime, iTime + headLength, st.s);
float tailMask = 1.0 - smoothstep(0.0, tailLength, iTime - st.s);
float flowMask = clamp(tailMask * (1.0 - headMask), 0.0, 1.0);
// 应用遮罩增强发光
vec3 diffuseColor = baseColor;
vec3 emissionColor = baseColor * (glowFalloff * flowMask * 6.0);
float alpha = glowFalloff * flowMask * color.a * 0.6;
// 最终输出
material.diffuse = diffuseColor;
material.emission = emissionColor * 0.4;
material.alpha = alpha;
return material;
}
四、定义响应式属性(关联 Shader 参数)
为了让 color、glowPower、taperPower 三个参数支持 “修改后自动更新视图”,必须使用 Object.defineProperties 定义属性描述符 ——Cesium.createPropertyDescriptor 会自动绑定私有变量和 definitionChanged 事件,这是 Cesium 响应式机制的核心(ES6 类字段无法替代,因其不能定义属性描述符):
Object.defineProperties(PolylineFlowMaterialProperty.prototype, {
color: Cesium.createPropertyDescriptor("color"),
glowPower: Cesium.createPropertyDescriptor("glowPower"),
taperPower: Cesium.createPropertyDescriptor("taperPower"),
});
至此,自定义流动线材质的 “基础骨架” 已搭建完成,包括类结构、着色器核心、响应式属性定义。下一篇将聚焦构造函数、只读属性、核心方法的具体实现,以及最终的材质注册流程。
看不明白没关系,点这里可以查看视频解析👇
 发光流动线实例讲解,实现自定义 MaterialProperty(上)&spm=1001.2101.3001.5002&articleId=155226305&d=1&t=3&u=a1a2ea3ed0664aec84f53ebd4fca2c66)

被折叠的 条评论
为什么被折叠?



