书接上文:
我们实现了白膜的颜色修改,那如何实现白膜渐变效果呢?这里就涉及到了模型坐标系的概念,要想实现渐变效果,需要找到
- 模型位置
positionMC - 模型的坐标轴-即竖直方向
Cesium 使用多个坐标系系统:
1. 主要坐标系概览
| 坐标系 | 缩写 | 用途 | 特点 |
|---|---|---|---|
| 世界坐标系 | WGS84 | 全球定位 | 经纬度高程 |
| 固定坐标系 | ECEF | 地心坐标 | 地心为原点 |
| 眼坐标系 | EC | 相机相对 | 相机为原点 |
| 模型坐标系 | MC | 模型局部 | 模型自身坐标 |
| 窗口坐标系 | Window | 屏幕像素 | 2D屏幕坐标 |
2. 详细坐标系说明
2.1 世界坐标系 (WGS84)
// 经纬度高程坐标系
const position = Cesium.Cartographic.fromDegrees(
longitude, // 经度 (-180 到 180)
latitude, // 纬度 (-90 到 90)
height // 高程 (米)
);
2.2 地心地固坐标系 (ECEF - Earth-Centered, Earth-Fixed)
// 以地心为原点的三维直角坐标系
const ecefPosition = Cesium.Cartesian3.fromDegrees(
longitude, latitude, height
);
// X: 指向本初子午线与赤道交点
// Y: 指向东经90度与赤道交点
// Z: 指向北极点
2.3 眼坐标系 (Eye Coordinates - EC)
// 以相机为原点的坐标系
// 在着色器中常用:normalEC, positionEC
2.4 模型坐标系 (Model Coordinates - MC)
// 模型自身的局部坐标系
// 在着色器中:positionMC, normalMC
// 原点通常是模型的中心或边界框中心
下面这里重点讲解模型坐标系:
3. 在自定义着色器中的坐标系
顶点着色器可用属性:
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
// 模型坐标系
vec3 positionMC = vsInput.attributes.positionMC; // 模型坐标位置
vec3 normalMC = vsInput.attributes.normalMC; // 模型坐标法线
// 世界坐标系相关
vec3 positionWC = vsOutput.positionWC; // 世界坐标位置
// 眼坐标系
vec3 positionEC = vsOutput.positionEC; // 眼坐标位置
}
片元着色器可用属性:
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
// 从顶点着色器传递的变量
vec3 positionEC = fsInput.attributes.positionEC; // 眼坐标位置
vec3 normalEC = fsInput.attributes.normalEC; // 眼坐标法线
// 模型坐标系(如果通过varying传递)
// vec3 positionMC = v_positionMC;
}
4. 坐标系转换
4.1 内置转换函数:
// 模型坐标 → 眼坐标
vec3 positionEC = czm_modelToEyeCoordinates(positionMC);
// 眼坐标 → 裁剪坐标
vec4 positionCC = czm_modelViewProjection * vec4(positionMC, 1.0);
// 眼坐标 → 世界坐标
vec3 positionWC = czm_eyeToWindowCoordinates(positionEC);
4.2 实际应用示例:
// 计算相机距离(用于雾效、LOD等)
float distanceToCamera = length(fsInput.attributes.positionEC);
// 基于世界坐标的高度着色
vec3 positionWC = fsInput.attributes.positionWC;
float altitude = positionWC.z; // 注意:这可能是地心坐标的高度
5. 确定坐标系方向的调试方法
坐标轴可视化
fragmentMain: 片元着色器的主函数,每个像素都会执行一次FragmentInput fsInput: 输入参数,包含像素的各种属性(位置、法线、UV等)inout czm_modelMaterial material: 输入输出参数,用于设置材质的最终外观positionMC: 模型坐标系(Model Coordinates)中的位置fsInput.attributes.positionMC: 从输入参数中获取当前像素在模型坐标系中的位置
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material){
//
vec3 positionMC = fsInput.attributes.positionMC;
// // 测试X轴 - 红色
// material.diffuse = vec3(positionMC.x * 0.01, 0.0, 0.0);
// // 测试Y轴 - 绿色
// // material.diffuse = vec3(0.0, positionMC.y * 0.01, 0.0);
// // 测试Z轴 - 蓝色
// material.diffuse = vec3(0.0, 0.0, positionMC.z * 0.01);
material.diffuse = vec3(
positionMC.x * 0.01, // X轴 - 红色
positionMC.y * 0.01, // Y轴 - 绿色
positionMC.z * 0.01 // Z轴 - 蓝色
);
}
可以很明显看到模型的中心点位置
测试X轴 - 红色(左右)
测试Y轴 - 绿色 (前后)
测试Z轴 - 蓝色(上下)
我这里是z轴是垂直上下方向,y轴是前后方向, x轴是左右方向
为什么 Z 轴是垂直方向?
这是因为:
positionMC是模型局部坐标系- 模型导入时可能使用了 Z-up 约定
- 不同的3D建模软件有不同的默认坐标系:
-
- 3ds Max, Blender: Z-up
- Maya, Cinema4D: Y-up
- Unity, Unreal: Y-up
6. 实现白膜竖直方向上的渐变
fragmentShaderText: `
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material){
vec3 positionMC = fsInput.attributes.positionMC;
material.diffuse = vec3(
0,
positionMC.z * 0.005,
0.5 + positionMC.z * 0.005
);
}
`,
7. 最佳实践建议
- 先测试确认坐标系方向再编写着色器
- 优先使用世界坐标系进行地理相关的计算
- 使用模型坐标系进行模型自身的特效
- 注意坐标系的缩放因子,不同模型可能有不同的单位