作者:kele
前言
在三维地理信息系统中,河流是重要的组成部分,如何在三维场景中展示河流成为我们重点探讨的对象;在以往的方法中,我们可以通过iDesktop制作水面特效来展示河流,但这种方式的局限性在于不能改变河流的走向以及水流的方向,今天,小编为大家介绍一种前端绘制河流的方法~
一、实现思路
1.绘制entity实体线
2.给实体线赋予动态材质贴图
3.根据相机高度改变实体线宽
其中主要的障碍应该是第二点,如何自定义动态纹理材质,我们一起来看下吧
二、实现代码
1.构建自定义动态纹理材质
color:材质颜色
duration:纹理流动循环周期
texture2D(image, vec2(fract(st.s5.0-time1.0), st.t)):纹理运动方向及速度
function PolylineTrailLinkMaterialProperty(color, duration) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._colorSubscription = undefined;
this.color = color;
this.duration = duration;
this._time = (new Date()).getTime();
}
Object.defineProperties(PolylineTrailLinkMaterialProperty.prototype, {
isConstant: {
get: function () {
return false;
}
},
definitionChanged: {
get: function () {
return this._definitionChanged;
}
},
color: Cesium.createPropertyDescriptor('color')
});
PolylineTrailLinkMaterialProperty.prototype.getType = function (time) {
return 'PolylineTrailLink';
};
PolylineTrailLinkMaterialProperty.prototype.getValue = function (time, result) {
if (!Cesium.defined(result)) {
result = {};
}
result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);
result.image = Cesium.Material.PolylineTrailLinkImage;
result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration;
return result;
};
PolylineTrailLinkMaterialProperty.prototype.equals = function (other) {
return this === other ||
(other instanceof PolylineTrailLinkMaterialProperty &&
Property.equals(this._color, other._color))
};
Cesium.PolylineTrailLinkMaterialProperty = PolylineTrailLinkMaterialProperty;
Cesium.Material.PolylineTrailLinkType = 'PolylineTrailLink';
Cesium.Material.PolylineTrailLinkImage = "./images/movingRiver.png";
Cesium.Material.PolylineTrailLinkSource = "\
uniform vec4 color;\n\
uniform float time;\n\
uniform sampler2D image;\n\
czm_material czm_getMaterial(czm_materialInput materialInput)\n\
{\n\
czm_material material = czm_getDefaultMaterial(materialInput);\n\
vec2 st = materialInput.st;\n\
vec4 colorImage = texture2D(image, vec2(fract(st.s*5.0-time*1.0), st.t));\n\
material.alpha = colorImage.a * color.a;\n\
material.diffuse = colorImage.rgb;\n\
return material;\n\
}";
Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailLinkType, {
fabric: {
type: Cesium.Material.PolylineTrailLinkType,
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 0.5),
image: Cesium.Material.PolylineTrailLinkImage,
time: 0,
repeat: 3
},
source: Cesium.Material.PolylineTrailLinkSource
},
translucent: function (material) {
return true;
}
});
2.绘制实体线,并赋予第一步定义的材质
river = viewer.entities.add({
name : 'river',
polyline : {
positions : new Cesium.Cartesian3.fromDegreesArrayHeights(positions),
width: 20,
followSurface : true,
material : new Cesium.PolylineTrailLinkMaterialProperty(Cesium.Color.SKYBLUE,10000),
clampToGround: true,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 30000)
}
});
三、控制河流宽度
因为polyline的线宽是像素宽度,不会随着场景高度变化而变化,导致在场景缩放过程中河流宽度不真实,我们用以下方法来动态改变河流宽度
scene.postRender.addEventListener(function(){ // 每一帧都去计算气泡的正确位置
if(flag === true){
var position = viewer.scene.camera._position;
var cartographic = Cesium.Cartographic.fromCartesian(position);
var currtenHeight = cartographic.height;
for(var i=1;i<=10;i++){
if(currtenHeight>=perHeight*i && currtenHeight<=(perHeight*i+perHeight)){
if(currtenHeight<3500){
river.polyline.width = maxWidth
}
else if(currtenHeight>3500 && river.polyline.width._value !== maxWidth-perWidth*i){
river.polyline.width = maxWidth-perWidth*i
}
}
}
}
});
四、效果图