这其实是一个很简单能实现的效果,百度搜索随便一搜会出现很多博客。

为什么还要再出一个?想在这里把我找到的实现此效果的方法及策略来一个综合对比。

个人觉得自己的实现方式相对来说性能会好一些。代码量相对少很多。

所有给出的代码直接粘贴到cesium官方沙盒示例里面就可以运行。

第一种实现方式:

百度搜索出来的,80%源代码粘贴过来的。大致看了下采用后处理方式渲染,必须开启深度检测。

代码中直接添加了1000个实体,很卡,可视化效果也一般般。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const viewer = new Cesium.Viewer("cesiumContainer");
 
viewer.scene.globe.depthTestAgainstTerrain = true;
 
var entity = viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(111.0, 40.0, 1500),
    billboard: {
        image: "../images/Cesium_Logo_overlay.png",
        scale: 1,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
    }
});
viewer.flyTo(entity);
 
for (var i = 0; i < 1000; i++) {
    var cartographicCenter = Cesium.Cartographic.fromDegrees(110 + Math.random() * 1,
        40 + Math.random() * 1,
        0);
    var scanColor = new Cesium.Color(0.0, 1.0, 0.0, 1);
    var lastStage = addCircleScanPostStage(viewer, cartographicCenter, 1000, scanColor, 4000);
}
 
//扩散效果Shader
function getScanSegmentShader() {
    var scanSegmentShader = "\n\
uniform sampler2D colorTexture;\n\
uniform sampler2D depthTexture;\n\
in vec2 v_textureCoordinates;\n\
uniform vec4 u_scanCenterEC;\n\
uniform vec3 u_scanPlaneNormalEC;\n\
uniform float u_radius;\n\
uniform vec4 u_scanColor;\n\
\n\
vec4 toEye(in vec2 uv,in float depth)\n\
{\n\
vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));\n\
vec4 posIncamera = czm_inverseProjection * vec4(xy,depth,1.0);\n\
posIncamera = posIncamera/posIncamera.w;\n\
return posIncamera;\n\
}\n\
\n\
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point)\n\
{\n\
vec3 v01 = point - planeOrigin;\n\
float d = dot(planeNormal,v01);\n\
return (point-planeNormal * d);\n\
}\n\
float getDepth(in vec4 depth)\n\
{\n\
float z_window = czm_unpackDepth(depth);\n\
z_window = czm_reverseLogDepth(z_window);\n\
float n_range = czm_depthRange.near;\n\
float f_range = czm_depthRange.far;\n\
return (2.0 * z_window - n_range - f_range)/(f_range-n_range);\n\
} \n\
void main()\n\
{\n\
out_FragColor = texture(colorTexture,v_textureCoordinates);\n\
float depth = getDepth(texture(depthTexture,v_textureCoordinates));\n\
vec4 viewPos = toEye(v_textureCoordinates,depth);\n\
vec3 prjOnPlane = pointProjectOnPlane(u_scanPlaneNormalEC.xyz,u_scanCenterEC.xyz,viewPos.xyz);\n\
float dis = length(prjOnPlane.xyz - u_scanCenterEC.xyz);\n\
if(dis<u_radius)\n\ {\n\ float f=1.0-abs(u_radius - dis )/ u_radius;\n\ f=pow(f,4.0);\n\
out_FragColor=mix(out_FragColor,u_scanColor,f);\n\ }\n\ } \n\ ";
    return scanSegmentShader;
}
 
/*
添加扩散效果扫描线
viewer
cartographicCenter 扫描中心
radius 半径 米
scanColor 扫描颜色
duration 持续时间 毫秒
*/
function addCircleScanPostStage(viewer, cartographicCenter, maxRadius, scanColor, duration) {
    var _Cartesian3Center = Cesium.Cartographic.toCartesian(cartographicCenter);
    var _Cartesian4Center = new Cesium.Cartesian4(_Cartesian3Center.x, _Cartesian3Center.y, _Cartesian3Center.z, 1);
 
    var _CartograhpicCenter1 = new Cesium.Cartographic(cartographicCenter.longitude, cartographicCenter.latitude, cartographicCenter.height + 500);
    var _Cartesian3Center1 = Cesium.Cartographic.toCartesian(_CartograhpicCenter1);
    var _Cartesian4Center1 = new Cesium.Cartesian4(_Cartesian3Center1.x, _Cartesian3Center1.y, _Cartesian3Center1.z, 1);
 
    var _time = (new Date()).getTime();
 
    var _scratchCartesian4Center = new Cesium.Cartesian4();
    var _scratchCartesian4Center1 = new Cesium.Cartesian4();
    var _scratchCartesian3Normal = new Cesium.Cartesian3();
 
 
    var ScanPostStage = new Cesium.PostProcessStage({
        fragmentShader: getScanSegmentShader(),
        uniforms: {
            u_scanCenterEC: function () {
                var temp = Cesium.Matrix4.multiplyByVector(viewer.camera._viewMatrix, _Cartesian4Center, _scratchCartesian4Center);
                return temp;
            },
            u_scanPlaneNormalEC: function () {
                var temp = Cesium.Matrix4.multiplyByVector(viewer.camera._viewMatrix, _Cartesian4Center, _scratchCartesian4Center);
                var temp1 = Cesium.Matrix4.multiplyByVector(viewer.camera._viewMatrix, _Cartesian4Center1, _scratchCartesian4Center1);
 
                _scratchCartesian3Normal.x = temp1.x - temp.x;
                _scratchCartesian3Normal.y = temp1.y - temp.y;
                _scratchCartesian3Normal.z = temp1.z - temp.z;
 
                Cesium.Cartesian3.normalize(_scratchCartesian3Normal, _scratchCartesian3Normal);
 
                return _scratchCartesian3Normal;
            },
            u_radius: function () {
                return maxRadius * (((new Date()).getTime() - _time) % duration) / duration;
            },
            u_scanColor: scanColor
        }
    });
 
    viewer.scene.postProcessStages.add(ScanPostStage);
    return ScanPostStage;
}

 

第二种方式:和第三种方式一样,扩展材质属性,唯一的区别在shder不一样,替换第三种方式的shader就可以看到效果。和第三种方式相比区别在不能支持多个波同时扩散。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
czm_material czm_getMaterial(czm_materialInput materialInput)
{
    czm_material material = czm_getDefaultMaterial(materialInput);
    material.diffuse = 1.5 * color.rgb;
 
    vec2 st = materialInput.st;
    float dis = distance(st, vec2(0.5, 0.5));
 
    float per = fract(time);
    if(dis > per * 0.5){
       //material.alpha = 0.0;
       discard;
    }else {
         material.alpha = color.a  * dis / per / 2.0;
    }
    return material;
}

  

  

第三种方式:这种方式在网上应该有类似的实现方案,就是扩展一个MaterialProperty,其中着色器代码自己加工修改过,能够自己自定义扩散圆数量,算法逻辑就不详细介绍,有需要的直接粘贴过去使用即可。循环最大值是写死的10,默认最大只支持9个圆扩散。这种方式加载1000个性能是没有影响的,就相当于加载了普通的1000个圆形实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const viewer = new Cesium.Viewer("cesiumContainer");
let Color = Cesium.Color;
let defaultValue = Cesium.defaultValue;
let defined = Cesium.defined;
let Event = Cesium.Event;
let createPropertyDescriptor = Cesium.createPropertyDescriptor;
let Property = Cesium.Property;
let Material = Cesium.Material;
 
var defaultColor = Color.WHITE;
 
function ElliposidWaveMaterialProperty(options) {
    options = defaultValue(options, defaultValue.EMPTY_OBJECT);
 
    this._definitionChanged = new Event();
    this._color = undefined;
    this._colorSubscription = undefined;
 
    this.color = options.color;
    this.count = options.count || 1;
    this.duration = Cesium.defaultValue(options.duration, 1e3);
    this.gradient = Cesium.defaultValue(options.gradient, 0.1);
    this._time = performance.now();
 
}
 
Object.defineProperties(ElliposidWaveMaterialProperty.prototype, {
    isConstant: {
        get: function () {
            return false;
        }
    },
    definitionChanged: {
        get: function () {
            return this._definitionChanged;
        }
    },
    color: createPropertyDescriptor("color")
});
 
ElliposidWaveMaterialProperty.prototype.getType = function (time) {
    return Material.EllipsoidWaveType;
};
 
ElliposidWaveMaterialProperty.prototype.getValue = function (time, result) {
    if (!defined(result)) {
        result = {};
    }
    result.color = Property.getValueOrClonedDefault(this._color, time, defaultColor, result.color);
 
    if (this._time === undefined) {
        this._time = time.secondsOfDay;
    }
    result.time = (performance.now() - this._time) / this.duration;
    result.count = this.count;
    result.gradient = 1 + 10 * (1 - this.gradient);
    return result;
};
 
ElliposidWaveMaterialProperty.prototype.equals = function (other) {
    return this === other || //
        (other instanceof ElliposidWaveMaterialProperty &&
            Property.equals(this._color, other._color));
};
 
Material.EllipsoidWaveType = "EllipsoidWave";
const ElliposidWaveMaterial = "czm_material czm_getMaterial(czm_materialInput materialInput) { \n\
    czm_material material = czm_getDefaultMaterial(materialInput); \n\
    material.diffuse = 1.5 * color.rgb; \n\
    vec2 st = materialInput.st; \n\
    float dis = distance(st, vec2(0.5, 0.5)); \n\
    float per = fract(time); \n\
    if (dis > 0.5) {  \n\
        discard;  \n\
    } else {  \n\
        float perDis = 0.5 / count; \n\
        float disNum; \n\
        float bl = .0; \n\
        for (int i = 0; i < 10; i++) {\n\
            if(float(i) > count) {\n\
                break;\n\
            }   \n\
            disNum = perDis * float(i) - dis + per / count; \n\
            if (disNum > 0.0) {  \n\
                if (disNum < perDis) {  \n\
                    bl = 1.0 - disNum / perDis; \n\
                } \n\
                else if(disNum - perDis < perDis) { \n\
                    bl = 1.0 - abs(1.0 - disNum / perDis);  \n\
                }  \n\
                material.alpha = pow(bl, gradient);  \n\
            }  \n\
        }  \n\
    }  \n\
    return material;  \n\
}";
 
Material._materialCache.addMaterial(Material.EllipsoidWaveType, {
    fabric: {
        type: Material.EllipsoidWaveType,
        uniforms: {
            color: new Color(1, 0, 0, 1.0),
            time: 1,
            count: 1,
            duration: 1000,
            gradient: 0.1
        },
        source: ElliposidWaveMaterial
    },
    translucent: function () {
        return true;
    }
});
 
for (var i = 0; i < 1000; i++) {
    let greenCircle = viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(110 + Math.random() * 1,
        40 + Math.random() * 1,
        10),
        name: "Green circle at height with outline",
        ellipse: {
            semiMinorAxis:1000.0,
            semiMajorAxis: 1000.0,
            height: 10.0,
            material: new ElliposidWaveMaterialProperty({
                color: Color.GREEN,
                count: 3,
                duration: 3000,
                gradient: 0.2,
            })
        },
    });
}
 
viewer.zoomTo(viewer.entities);