【问题标题】:Best and most performant implementation of dynamic shapes in cesium铯中动态形状的最佳和最高效的实现
【发布时间】:2015-12-28 11:00:59
【问题描述】:

我目前正在使用一个使用 Cesium 查看器的应用程序。我需要能够显示将动态更新的形状集合。我无法理解执行此操作的最佳方法。

我目前正在使用实体并使用 CallbackProperties 来更新形状。

你可以通过这个进入沙堡来了解我是如何做到这一点的。有一个多边形对象被用作 cesiumCallback 的基础,它正在被另一段代码编辑。 (用setTimeout模拟)

var viewer = new Cesium.Viewer('cesiumContainer', {});

var polygon = {};
polygon.coordinates = [
    {longitude: 0, latitude: 0, altitude: 0},
    {longitude: 10, latitude: 10, altitude: 0},
    {longitude: 10, latitude: 0, altitude: 0}
];


// converts generic style options to cesium one (aka color -> material)
var polOpts = {};

 // function for getting location
polOpts.hierarchy = new Cesium.CallbackProperty(function() {
  var hierarchy = [];
  for (var i = 0; i < polygon.coordinates.length; i++) {
       var coordinate = polygon.coordinates[i];
       hierarchy.push(Cesium.Cartesian3.fromDegrees(coordinate.longitude, coordinate.latitude, coordinate.altitude));
  }
  return hierarchy;
}, false);

viewer.entities.add({polygon: polOpts});

setInterval(function(polygon){
       polygon.coordinates[0].longitude--;
}.bind(this, polygon), 1000);

传入的多边形是一个通用描述多边形的类,所以它有一个坐标和样式选项的数组,还有一个调用这个方法的渲染方法renderPolygon传入。 这种渲染形状的方法适用于我需要的一切,但性能不是很好。形状更新有两种情况,一种形状会在很长一段时间内更新,速度很慢,比如每隔几秒一次。另一种是形状会在几秒钟内更新很多次,比如数千次,然后在很长一段时间内不会再改变,如果有的话。

我对如何解决这个问题有两个想法。

想法 1: 有两个方法,一个 renderDynamicPolygon 和一个 renderStaticPolygon。 renderDynamicPolygon 方法将使用 cesiumCallbackProperties 完成上述功能。这将用于在短时间内更新多次的形状。 一旦更新完成,renderStaticPolygon 方法将使用常量值替换使用 callbackProperties 的实体属性。

这会产生很多其他工作来确保形状处于正确状态,并且无助于长时间缓慢更新的形状。

想法 2: 与原语的工作方式类似,我尝试删除旧实体,并在每次需要更新时使用其更新的属性再次添加它,但这会导致闪烁,并且与原语不同,我找不到实体的异步属性。

我也尝试过使用原语。它对折线非常有用,我只需删除旧的并添加具有更新属性的新的。我还使用 async = false 来确保没有闪烁。我在这里遇到的这个问题并不是所有的形状都可以使用基元创建。 (这是真的吗?)

我尝试的另一件事是使用几何和外观的几何实例。在浏览了 cesium 网站上的教程后,我能够渲染一些形状,并且可以更新外观,但发现几乎不可能弄清楚如何正确更新形状,而且很难让它们看起来正确。形状需要具有正确的形状、填充颜色和不透明度以及描边颜色、不透明度和重量。我尝试使用polygonOutlineGeometry,但没有运气。

实现这一点的最佳方法是什么?这些选项之一是否朝着正确的方向前进,或者是否有其他一些我尚未发现的方法?


[编辑] 我添加了我得到的答案,但仍然不完整并正在寻找答案。

【问题讨论】:

  • 不要绑定,而是以粗略的方式使用实体 API。 GetByID,然后更新实体。如果您有很多每秒更新很多次的形状,我建议您实现自己的着色器。

标签: cesium


【解决方案1】:

我已经想出了一个很好的解决方案,但它仍然存在一个小问题。

我也采用了显示实体的方式。我正在调用一种渲染和一种油漆。渲染使用Cesium.CallbackPropertyisConstant 属性true,并使用isConstantProperty false 进行绘制。

然后我创建了一个函数来将实体从渲染更改为绘制,反之亦然。它遍历实体回调属性,并使用 setCallback 属性用正确的函数和 isConstant 值覆盖该属性。

示例: 我根据我定义的圆形对象创建了一个椭圆。

// isConst is True if it is being "painted" and false if it is being "rendered"
ellipse: lenz.util.extend(this._getStyleOptions(circle), {
  semiMinorAxis: new Cesium.CallbackProperty(
    this._getRadius.bind(this, circle),
    isConst
  ),
  semiMajorAxis: new Cesium.CallbackProperty(
    this._getRadius.bind(this, circle),
    isConst
  ),
})

因此,当形状被更新时(当用户绘制形状时),形状被渲染,isConstant 为假。 然后当绘制完成后,它会使用如下代码转换为绘制版本:

existingEntity.ellipse.semiMinorAxis.setCallback(
  this._getRadius.bind(this, circle),
  isConst
);
                            existingEntity.ellipse.semiMajorAxis.setCallback(
  this._getRadius.bind(this, circle, 1),
  isConst
);

这在性能方面非常有效。我能够绘制数百个形状,而框架根本不会掉很多。我附上了一张铯地图的屏幕截图,其中包含我更改前后的 612 个实体,帧速率在使用 chrome 渲染工具的右上角。

之前:锁定在 fps 0.9 注意:我编辑了其余的用户界面,女巫使地球看起来被切断了,对不起

更改后:fps 保持在 59.9,几乎完美!

每当实体从使用常量“转换”为非常量回调属性时,它和所有其他相同类型的实体都会闪烁然后再次打开。我找不到更好的方法来进行这种转换。我觉得我一定还缺少一些东西。

【讨论】:

    【解决方案2】:

    您可以尝试使用PositionPropertyArray 作为多边形的层次结构,SampledPositionProperty 用于任何动态位置,ConstantPositionProperty 用于任何静态位置。我不确定它是否会比您的解决方案更好,但它可能值得测试。以下是您可以将其粘贴到铯沙堡中的示例:

    var viewer = new Cesium.Viewer('cesiumContainer', {});
    
    // required if you want no interpolation of position between times
    var noInterpolation = {
        type: 'No Interpolation',
    
        getRequiredDataPoints: function (degree) {
            return 2;
        },
    
        interpolateOrderZero: function (x, xTable, yTable, yStride, result) {
            if (!Cesium.defined(result)) {
                result = new Array(yStride);
            }
    
            for (var i = 0; i < yStride; i++) {
                result[i] = yTable[i];
            }
    
            return result;
        }
    };
    
    var start = viewer.clock.currentTime;
    
    // set up the sampled position property
    var sampledPositionProperty = new Cesium.SampledPositionProperty();
    sampledPositionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
    sampledPositionProperty.addSample(start, new Cesium.Cartesian3.fromDegrees(0, 0));  // initial position
    
    sampledPositionProperty.setInterpolationOptions({
        interpolationAlgorithm: noInterpolation
    }); 
    
    // set up the sampled position property array
    var positions = [
        sampledPositionProperty,
        new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 10)),
        new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 0))
    ];
    
    // add the polygon to Cesium viewer
    var polygonEntity = new Cesium.Entity({
        polygon: {
            hierarchy: new Cesium.PositionPropertyArray(positions)
        }
    });
    
    viewer.zoomTo(viewer.entities.add(polygonEntity));
    
    // add a sample every second
    var counter = 1;
    setInterval(function(positionArray) {
        var time = new Cesium.JulianDate.addSeconds(start, counter, new Cesium.JulianDate());
        var position = new Cesium.Cartesian3.fromDegrees(-counter, 0);
        positionArray[0].addSample(time, position);
        counter++;
    }.bind(this, positions), 1000);
    

    这样做的一个好处是您可以将时间线的开始/结束时间设置为合理的范围,并使用它在样本范围内的任何时间查看您的多边形,这样您就可以查看多边形的历史历史(参见 @ 987654324@ 了解如何更改时间线开始/结束时间)。此外,您不需要使用计时器来设置位置,时间是内置在SampledPositionProperty 中的(尽管您仍然可以异步添加样本)。

    但是,这也意味着位置取决于时间轴中的当前时间,而不是实时数组值。如果您不是一次添加所有样本,您可能需要在某个地方跟踪时间。

    我以前也从未使用过省略号,但 semiMinorAxissemiMajorAxis 是属性,因此您仍然可以使用 SampledProperty

    当然,如果仍然存在性能问题,这并不重要。希望它会有所改进,因为您不需要在每个回调中从头开始重新创建数组,并且根据您获取数据以更新多边形的方式,您可能可以一次添加多个样本。这只是猜测,但需要考虑。

    编辑

    Cesium 可以处理添加到采样位置的大量样本,例如在上面的代码中,如果您将一百万个样本添加到该位置,则需要几秒钟才能将它们全部加载,但可以随时渲染多边形而无需任何性能问题。要对此进行测试,而不是使用计时器添加样本,只需将它们全部直接添加到属性中即可。

    for (var i = 0; i < 1000000; i++) {
        var time = new Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
        var position = new Cesium.Cartesian3.fromDegrees(-(i % 2), 0);
        positions[0].addSample(time, position);
    }
    

    但是,如果您遇到内存问题currently there is no way to remove samples from a position property without accessing private variables。一种解决方法是定期创建一个包含新位置属性的新数组,并使用先前位置属性数组的 setValue() 方法清除先前的值,或者可能使用 TimeIntervalCollectionProperty as in this answer 并使用 removeInterval 删除时间间隔方法。

    【讨论】:

    • 感谢您的回复,但使用polygonEntity.polygon.hierarchy._value[0].addSample(time, position); 似乎是个坏主意。访问私人成员可能很危险。此外,我正在使用的一些形状会有数千次更新,并且保持这些历史记录可能会很快加起来。
    • 没问题!你是对的,为了方便,我使用了私有成员,这很糟糕,但你真的只需要保留对你用来制作 PositionPropertyArray 的数组的引用来添加样本。我在上面对其进行了编辑以显示这一点,并考虑了您的历史问题。
    猜你喜欢
    • 1970-01-01
    • 2021-10-02
    • 2011-09-16
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    • 2018-11-19
    • 1970-01-01
    相关资源
    最近更新 更多