引言
在GIS应用中,常常需要显示带有方向指示的路线,例如车辆行驶轨迹、河流流向、风向等。传统的线条无法直观地表示方向信息,而通过在线路上添加方向箭头,可以清晰地展示运动或流动的方向。本文将详细介绍如何在Cesium中实现这种带有方向指示的路线效果。
技术原理
核心概念
实现带方向路线的关键在于自定义材质(Custom Material)。Cesium提供了强大的材质系统,允许开发者通过GLSL着色器来自定义渲染效果。在这个实现中,我创建了一个ImageLineMaterial材质类,它会在每段线路上周期性地显示箭头图案,从而指示方向。
实现思路
- 创建一个自定义材质类,继承Cesium的材质属性接口
- 定义GLSL片段着色器,在其中实现箭头绘制逻辑
- 利用线段的角度信息对箭头进行旋转,使其始终指向前进方向
- 通过纹理采样和混合算法实现虚线与箭头的交替显示
代码实现详解
1. 自定义材质类定义
kotlin
class ImageLineMaterial {
constructor(opt) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
// 初始化配置参数
this.color = opt.color || defaultColor; // 箭头颜色
this._image = opt.image || defaultImage; // 箭头纹理图片
this._arrowLength = opt.arrowLength || defaultArrowLength;
this._dashLength = opt.dashLength || defaultdashLength;
}
// 获取材质类型
getType() {
return 'ImageLine';
}
// 获取材质值,供Cesium渲染时调用
getValue(time, result) {
if (!Cesium.defined(result)) {
result = {};
}
result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, defaultColor, result.color);
result.image = this._image;
result.dashLength = this._dashLength;
result.arrowLength = this._arrowLength;
return result;
}
// 比较两个材质是否相等
equals(other) {
return (
this === other ||
(other instanceof ImageLineMaterial &&
Cesium.Property.equals(this._color, other._color))
);
}
}
2. GLSL着色器实现
这是整个实现的核心部分,通过GLSL代码实现箭头的绘制:
ini
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
// 使用v_polylineAngle变量获取线段角度,并旋转坐标系
vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy;
// 计算像素级别的长度参数
float pixelDashLength = dashLength * czm_pixelRatio;
float pixelArrowLength = arrowLength * czm_pixelRatio;
float pixelSegmentLength = pixelDashLength + pixelArrowLength;
// 计算当前像素在重复单元中的位置
float pixelXOfSegment = fract(pos.x / pixelSegmentLength) * pixelSegmentLength;
float textureS = (pixelXOfSegment - pixelDashLength) / pixelArrowLength;
vec4 txColor;
vec4 outColor;
// 如果在箭头区域内,使用纹理颜色;否则使用基础颜色
if (textureS > 0.0) {
txColor = texture(image, vec2(textureS, st.t));
outColor = txColor * txColor.a + color * (1. - txColor.a);
} else {
txColor = color;
outColor = color;
}
// 边缘抗锯齿处理
float dist = min(st.t, abs(st.t - 1.));
vec4 outsideColor = vec4(0.0);
outColor = czm_antialias(outsideColor, txColor, outColor, dist);
// Gamma校正
outColor = czm_gammaCorrect(outColor);
material.diffuse = outColor.rgb;
material.alpha = outColor.a;
return material;
}
3. 注册材质到Cesium
php
// 注册材质到Cesium材质缓存中
Cesium.Material._materialCache.addMaterial('ImageLine', {
fabric: {
type: 'ImageLine',
uniforms: {
color: defaultColor,
image: defaultImage,
dashLength: 20.0,
arrowLength: 16.0,
},
source: `/* GLSL源码 */`
}
});
关键技术点解析
1. 线段角度计算
v_polylineAngle是Cesium内置的变量,它包含了线段的角度信息。通过这个变量,我们可以让箭头始终指向线段的前进方向。
2. 坐标变换与旋转
scss
glsl
mat2 rotate(float rad) {
float c = cos(rad);
float s = sin(rad);
return mat2(
c, s,
-s, c
);
}
vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy;
这段代码实现了坐标系的旋转,确保箭头能够按照线段的实际方向进行旋转。
3. 周期性模式生成
通过计算fract(pos.x / pixelSegmentLength)来实现虚线与箭头的周期性交替显示。
4. 纹理采样与混合
ini
glsl
if (textureS > 0.0) {
txColor = texture(image, vec2(textureS, st.t));
outColor = txColor * txColor.a + color * (1. - txColor.a);
} else {
outColor = color;
}
根据位置判断是否处于箭头区域,如果是则使用纹理图像,否则使用基础颜色形成虚线效果。
使用示例
less
// 创建带方向箭头的路线
viewer.entities.add({
name : 'Directional Polyline',
polyline : {
positions : Cesium.Cartesian3.fromDegreesArray([
-75.0, 35.0,
-125.0, 35.0,
-125.0, 30.0
]),
width : 8,
material : new Cesium.ImageLineMaterialProperty({
color : Cesium.Color.RED.withAlpha(0.7),
image : 'arrow-image-url.png',
dashLength : 20,
arrowLength : 16
})
}
});
性能优化考虑
- 纹理复用:箭头纹理可以在多个实体间共享,减少内存占用
- 精度控制:合理设置箭头密度,避免过度渲染
- LOD处理:根据视距调整箭头显示密度
总结
通过自定义材质的方式,成功实现了在Cesium中显示带有方向指示的路线。这种方法利用了Cesium强大的材质系统和GLSL着色器,能够高效地渲染大量带方向的路线。该技术在轨迹回放、流向可视化、路径规划等场景中有广泛应用价值。
关键优势包括:
- 方向指示清晰直观
- 渲染性能良好
- 可定制性强(颜色、大小、密度可调)
- 与Cesium原生API无缝集成
这种实现方式为GIS可视化提供了更加丰富的表达手段,提升了数据的可读性和用户体验。