<think>我们正在使用Cesium创建动态线或流动线效果。这种效果通常用于表示运动方向,如河流、轨迹或管道流动。
思路:我们可以使用Cesium的PolylineGeometry和材质(Material)来实现,通过自定义材质并随时间更新材质的属性(如偏移),从而实现流动效果。
步骤:
1. 创建折线(Polyline)并应用材质。
2. 材质使用条纹(Stripe)或自定义着色器(Shader)来创建动态效果。
3. 在更新函数中不断改变材质属性的偏移值,例如移动纹理坐标。
推荐方法:使用Cesium的PolylineDashMaterialProperty或自定义MaterialProperty。
这里我们使用自定义的MaterialProperty来实现流动线效果,因为这样更灵活。
具体步骤:
a. 定义自定义的MaterialProperty,它继承自Cesium.MaterialProperty。
b. 在构造函数中设置必要的参数,如颜色、速度、图像(用于纹理)等。
c. 实现getType函数返回着色器类型。
d. 实现getValue函数,根据时间更新材质属性(例如纹理偏移)。
e. 在着色器中,使用纹理偏移来模拟流动效果。
但是,为了简单起见,我们也可以使用已有的材质,比如`PolylineGlowMaterialProperty`结合动态偏移,但Cesium标准材质中并没有直接支持动态流动的材质。因此,自定义材质是更好的选择。
另一种替代方案:使用Cesium.Primitive和GeometryInstance创建折线,并应用自定义着色器材质。
下面是一个使用自定义MaterialProperty的示例:
首先,我们定义一个新的MaterialProperty,名为`PolylineFlowMaterialProperty`。
然后,在场景中创建折线,并应用这个材质。
由于代码较长,我们将分步骤进行。
注意:以下代码基于Cesium 1.75或更高版本。
步骤1:定义材质属性
```javascript
class PolylineFlowMaterialProperty {
constructor(options) {
options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._speed = undefined;
this._percent = undefined;
this._gradient = undefined;
this.color = options.color || Cesium.Color.WHITE;
this.speed = options.speed || 1.0;
this.percent = options.percent || 0.03; // 流动线占总长度的比例
this.gradient = options.gradient || 0.1; // 颜色渐变程度
}
get isConstant() {
return false;
}
get definitionChanged() {
return this._definitionChanged;
}
getType(time) {
return 'PolylineFlow';
}
getValue(time, result) {
if (!result) {
result = {};
}
result.color = Cesium.Property.getValueOrUndefined(this._color, time);
result.speed = this._speed;
result.percent = this._percent;
result.gradient = this._gradient;
return result;
}
equals(other) {
return this === other ||
(other instanceof PolylineFlowMaterialProperty &&
Cesium.Property.equals(this._color, other._color) &&
this._speed === other._speed &&
this._percent === other._percent &&
this._gradient === other._gradient)
}
}
// 然后,我们需要注册这个材质类型,并提供着色器代码
Cesium.Material.PolylineFlowType = 'PolylineFlow';
Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineFlowType, {
fabric: {
type: 'PolylineFlow',
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
speed: 10.0,
percent: 0.03,
gradient: 0.1
},
source: `
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
// 计算流动效果
float t = fract(czm_frameNumber * speed / 1000.0);
// 当前点在st上的位置,st.s是沿着线的方向(0到1),st.t是垂直于线的方向(-1到1,但我们这里用0到1)
// 我们想要的效果是:一段线段(长度为percent)从0到1移动
// 计算当前点是否在流动段内
float alpha = 1.0;
if (st.s < t) {
alpha = 1.0 - (t - st.s) / percent;
} else {
alpha = 1.0 - (1.0 + t - st.s) / percent;
}
// 使用渐变
alpha = clamp(alpha, 0.0, 1.0);
alpha = pow(alpha, gradient);
material.alpha = alpha;
material.diffuse = color.rgb;
return material;
}
`
},
translucent: function(material) {
return true;
}
});
```
步骤2:创建折线并应用材质
```javascript
// 创建查看器
var viewer = new Cesium.Viewer('cesiumContainer');
// 创建折线
var polyline = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
0, 0,
10, 0,
10, 10,
20, 20
]),
width: 10,
material: new PolylineFlowMaterialProperty({
color: Cesium.Color.RED,
speed: 5.0,
percent: 0.05,
gradient: 0.5
})
}
});
```
注意:上面的着色器代码只是一个简单示例,实际效果可能需要调整。另外,由于折线材质在WebGL中的限制,我们使用`materialInput.st`来获取纹理坐标,其中st.s是沿着折线的长度方向(0到1),st.t是宽度方向(-1到1,但我们通常取绝对值)。
上述代码中,我们通过`czm_frameNumber`和`speed`来计算时间因子`t`,然后根据当前点的st.s坐标与t的关系计算透明度,从而形成流动效果。
但是,请注意,这种方法在折线很长时可能会有性能问题,因为每一帧都在更新。另外,折线顶点过多时,可能需要考虑分段。
替代方案:使用GroundPolylinePrimitive(如果是在地面上)以获得更好的性能。
另外,Cesium官方示例中有一个“动态线”的示例,使用的是类似的方法,但可能更完善。可以参考:https://sandcastle.cesium.com/?src=Polyline%20Glow.html 和 https://sandcastle.cesium.com/?src=Materials.html
但是,请注意,上述自定义材质属性代码可能需要根据具体需求进行调整。
如果希望线头是渐变的,也可以修改着色器代码,比如在流动段的前端和后端都做渐变。
由于自定义着色器比较复杂,另一种简单的做法是使用PolylineTrailLink,但Cesium本身不提供,我们可以参考社区的实现。
社区实现参考:https://github.com/zhangti0708/cesium-examples/blob/master/effects/PolylineTrailLinkMaterialProperty.js
下面是一个社区常用的流动线材质属性(PolylineTrailMaterialProperty)的代码:
```javascript
class PolylineTrailMaterialProperty {
constructor(options) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._colorSubscription = undefined;
this.color = options.color;
this.duration = options.duration;
this.trailImage = options.trailImage || require('./img/colors.png'); // 需要一个纹理图片
this._time = (new Date()).getTime();
}
get isConstant() {
return false;
}
get definitionChanged() {
return this._definitionChanged;
}
getType(time) {
return 'PolylineTrail';
}
getValue(time, result) {
if (!result) {
result = {};
}
result.color = Cesium.Property.getValueOrUndefined(this._color, time);
result.image = this.trailImage;
result.time = ((new Date()).getTime() - this._time) / this.duration;
return result;
}
equals(other) {
return this === other ||
(other instanceof PolylineTrailMaterialProperty &&
Cesium.Property.equals(this._color, other._color) &&
this.duration === other.duration &&
this.trailImage === other.trailImage)
}
}
// 注册材质
Cesium.Material.PolylineTrailType = 'PolylineTrail';
Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailType, {
fabric: {
type: 'PolylineTrail',
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
image: null,
time: 0
},
source: `
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
float time = fract(time);
// 根据st.s和time计算纹理坐标
vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
material.alpha = colorImage.a * color.a;
material.diffuse = color.rgb * colorImage.rgb;
return material;
}
`
},
translucent: function(material) {
return true;
}
});
```
使用纹理图片的好处是可以做出更复杂的流动效果,比如带有头部和尾部的渐变。
注意:第二种方法需要一张纹理图片,并且需要确保图片被正确加载。
总结:在Cesium中创建动态流动线,主要依赖于自定义材质属性,并在着色器中根据时间改变纹理坐标或透明度。
根据项目需求选择合适的方法。如果不需要复杂效果,可以使用第一种方法(纯色流动);如果需要更美观的效果,可以使用第二种方法(纹理流动)。
最后,别忘了在每一帧中更新材质属性(通过getValue中的time),这样效果才会动起来。
以上代码可能需要根据实际项目进行调整和优化。</think>在 Cesium 中创建动态流动线效果,可以通过以下方法实现:
### 方法 1:使用材质动态偏移(推荐)
```javascript
// 创建实体
const flowLine = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
116.4, 39.9,
121.5, 31.2,
113.5, 22.2
]),
width: 8,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.2,
color: Cesium.Color.CYAN,
// 关键:使用动态材质
taperPower: 0.5 // 添加锥形效果
})
}
});
// 创建流动材质
const flowMaterial = new Cesium.Material({
fabric: {
type: 'FlowLine',
uniforms: {
color: new Cesium.Color(0.0, 0.8, 1.0, 1.0),
speed: 5.0,
image: Cesium.Material.createImage('../images/flowTexture.png') // 条纹纹理
},
source: `
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
// 动态偏移纹理坐标
float time = fract(czm_frameNumber * speed / 1000.0);
vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
material.alpha = colorImage.a * color.a;
material.diffuse = color.rgb * colorImage.rgb;
return material;
}
`
}
});
// 应用材质
flowLine.polyline.material = flowMaterial;
```
### 方法 2:使用原生材质属性
```javascript
const movingLine = viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
120.0, 35.0, 100000,
125.0, 40.0, 100000
]),
width: 12,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.YELLOW,
dashLength: 16.0,
dashPattern: 0xFF00FF00 // 自定义虚线模式
})
}
});
// 动态更新虚线偏移
viewer.scene.preUpdate.addEventListener(() => {
const dashPattern = movingLine.polyline.material.dashPattern.getValue();
movingLine.polyline.material.dashPattern =
new Cesium.ConstantProperty(rotateDashPattern(dashPattern));
});
function rotateDashPattern(pattern) {
return (pattern >> 1) | ((pattern & 1) << 31);
}
```
### 关键说明:
1. **纹理准备**:
- 创建条纹纹理(如 32x32 透明 PNG)
- 使用渐变效果增强流动感
2. **性能优化**:
```javascript
// 使用GroundPolylinePrimitive提升性能
const primitive = new Cesium.GroundPolylinePrimitive({
geometryInstance: new Cesium.GeometryInstance({
geometry: new Cesium.GroundPolylineGeometry({
positions: positions,
width: 8.0
})
}),
appearance: new Cesium.PolylineMaterialAppearance({
material: flowMaterial
})
});
viewer.scene.primitives.add(primitive);
```
3. **参数调整**:
- `speed`:控制流动速度
- `taperPower`:调整尾部渐隐效果(0-1)
- `glowPower`:添加辉光强度(0-1)
### 效果增强技巧:
```javascript
// 添加脉冲效果
flowMaterial.uniforms.pulse = function() {
return 0.5 + 0.5 * Math.sin(Cesium.JulianDate.now().secondsOfDay);
};
// 动态改变颜色
Cesium.knockout.track(flowMaterial.uniforms);
viewer.clock.onTick.addEventListener(() => {
flowMaterial.uniforms.color.alpha = pulse();
});
```
### 示例纹理:
创建 `flowTexture.png`(16x16 像素):
- 从左到右:透明 → 蓝色 → 白色 → 蓝色 → 透明
- 使用渐变工具创建线性渐变效果
[^1]: 完整实现参考 Cesium Sandcastle 的 "Polyline Materials" 示例
[^2]: 高级效果实现参考 cesium-ground-polyline 库