Shader 基础与 Cesium 实现
真实世界中物体对光的反应各异,模拟这些特性需要定义材质属性。OpenGL 通常包含环境光、漫反射和镜面高光。Cesium 的 czm_material 结构体与此类似,但移除了显式的 ambient 字段,转而通过光照计算逻辑处理,并增加了法向量、自发光和透明度支持。
struct czm_material {
vec3 diffuse;
float specular;
float shininess;
vec3 normal;
vec3 emission;
float alpha;
};
在片段着色器中,Cesium 使用 czm_phong 函数计算最终颜色。它结合了太阳和月亮的方向,根据材质属性混合环境、漫反射和镜面反射分量。注意这里的环境光默认取漫反射的一半,这是一种简化的物理近似,实际运行时你会发现它对暗部细节影响很大。
Fabric 材质定义系统
为了简化开发,Cesium 引入了 Fabric 系统,允许通过 JSON 配置描述材质。内置了十八种类型,如 ImageType、ColorType、NormalMapType 等。开发者可以直接指定 type 和 uniforms,也可以组合多个材质层。
例如叠加漫反射贴图和法线贴图:
primitive.appearance.material = new Cesium.Material({
fabric : {
materials : {
applyDiffuseMaterial : { type : 'DiffuseMap', uniforms : { image : '../images/bumpmap.png' } },
normalMap : { type : 'NormalMap', uniforms : { image : '../images/normalmap.png', strength : 0.6 } }
},
components : {
diffuse : 'diffuseMaterial.diffuse',
specular : 0.01,
normal : 'normalMap.normal'
}
}
});
这种组合方式非常灵活,你可以把不同的纹理映射到材质的不同通道上,而不需要写复杂的自定义 Shader。
Material 内部机制
当调用 Material.fromType 时,系统会加载对应的预定义配置。核心在于 initializeMaterial 函数,它负责生成着色器源码。createMethodDefinition 会根据 components 配置拼接获取材质的 GLSL 代码,createUniforms 则声明 uniform 变量并建立映射关系。
这里有个细节,Cesium 会给每个 uniform 分配一个唯一的标号(比如 color_0),避免命名冲突。这种设计让自定义材质变得灵活,只需关注数据输入,底层渲染细节由框架接管。
Appearance 与渲染流程
Material 仅负责片段着色器的颜色计算,而 Appearance 封装了整个图元的顶点与片段着色器逻辑,并管理渲染状态(RenderState)。例如 EllipsoidSurfaceAppearance 会指定特定的顶点格式和剔除策略。
在 Primitive.update 阶段,Appearance 会合并 Material 的 Uniforms,并根据透明性设置渲染队列(Translucent/Opaque),最终生成 Command 提交给 GPU。理解这一分层架构,有助于在复杂场景下优化性能或定制特殊效果。如果你发现材质不显示,通常先检查 Appearance 的 RenderState 是否被正确传递,而不是只盯着 Material 本身。


