这其实是一个很简单能实现的效果,百度搜索随便一搜会出现很多博客。
为什么还要再出一个?想在这里把我找到的实现此效果的方法及策略来一个综合对比。
个人觉得自己的实现方式相对来说性能会好一些。代码量相对少很多。
所有给出的代码直接粘贴到cesium官方沙盒示例里面就可以运行。
第一种实现方式:
百度搜索出来的,80%源代码粘贴过来的。大致看了下采用后处理方式渲染,必须开启深度检测。
代码中直接添加了1000个实体,很卡,可视化效果也一般般。
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就可以看到效果。和第三种方式相比区别在不能支持多个波同时扩散。
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个圆形实例。
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);