是否有任何好的技术可以保持几乎排序的近乎整理的数据?

时间:2012-06-26 18:27:38

标签: algorithm sorting opengl data-structures 3d

简短版本:

我正在寻找一种技术,即使数值略有变化,也可以随着时间的推移以接近排序的顺序保存接近排序的数据。

以下是该场景:

在3D图形世界中,在绘制之前从前到后订购对象通常是有益的。当场景发生变化或场景视图发生变化时,此数据可能需要重新排序,但通常非常接近排序顺序(即帧之间不会发生很大变化)。按排序顺序,数据完全也不重要。最糟糕的事情是渲染多边形然后完全隐藏。这是一个小小的性能打击,但不是世界末日。

考虑到这一点,是否可以提前对数据进行排序,然后每帧对数据应用一个最小补丁,以确保数据主要 排序?在这种情况下,如果大多数对象按升序排列,则数据将被视为主要排序。也就是说,距离它正确位置10步的1个物体比距离它们正确位置1步的10个物体更好(10倍更好)。

值得注意的是,数据可以继续半定期修补,因为数据通常每秒渲染30次(左右)。只要计算有效,它就可以继续进行,直到更改停止并且列表完全排序。

现有想法:

我对这个问题的膝盖反应是:

  1. 在加载数据时对数据应用n log n排序,并对大量更改(我可以很容易地跟踪)进行排序。

  2. 当数据开始缓慢变化时(例如当场景旋转时),对数据应用某种(线性)传递来交换后向邻居并尝试维持排序顺序(我认为这基本上是shell sort - 也许有一个更好的算法用于这个单一的传递。

  3. 继续每帧进行一次局部排序,直到更改停止并且数据完全排序

  4. 返回步骤2并等待更多更改。

5 个答案:

答案 0 :(得分:5)

如果输入主要是排序的,则在O(n)时间内运行各种排序;如果数据未排序,则有O(n log n)。听起来你可以很容易地使用它。 Timsort是这样的一种,我相信,现在是python和java中的默认排序。 Smoothsort是另一个很容易实现的方法。

答案 1 :(得分:3)

根据您的描述,如果排序顺序发生变化,您无需更改数据本身。例如。您更改了相机,因此即使您没有修改任何多边形,排序顺序也应该更改。

如果是这样,您无法直接检测到排序顺序更改。如果可以的话,我会为多边形列表创建存储桶,并在触摸该存储桶中的“足够多”多边形时使用存储桶。

但是我认为你的系统不能这样运作。排序由视口决定。在那种情况下,排序前面的多边形比最后的多边形更重要。

所以我将聚合列表分成五等或类似的东西。从前到后,前五分之一是最靠近相机的部分。我每帧完全对第一段进行排序。我将第二段划分为子段 - 再说5次 - 并且每帧对每个子段进行排序,使得第5个帧的每5帧完全排序。将第三到第五段分成15个子段,每个帧每5帧进行一次,使其余每75帧完全排序。以60 fps的速度,你可以让显示列表每秒完全使用一次。

优先考虑列表前面的好处是, 1.前面的多边形在屏幕上往往会变大,并且会更频繁地无法进行深度测试。列表末尾的错误订单往往不仅无关紧要。 2.由于相机的更改,列表的前面更容易出现排序变化。

同样选择那些具有一点重叠的线段范围,以便多边形可以以2种方式迁移到正确的线段。

@OP:再考虑一下。您可能更关心的是分类成本保持有限 - 而不是随着场景复杂性而爆炸。特别是因为一个非常复杂的场景应该 - 令人惊讶地 - 不太容易受到不良影响(因为通常聚合物会变小)。

您可以定义每帧愿意执行的固定数量的排序。使用50%的预算作为您可以承受的最重要的预算,25%的预算用于排序下一个区域,25%用于平均花费在其余区域。

假设您预算每帧排序1000个多边形,并且场景中有10000个多边形。每帧排序前500个多边形。每隔十十帧为下一个区域排序250个多边形。所以第1帧为501-750,第2帧为751-1000等。然后将列表的其余部分划分为250个帧段,并对它们进行循环排序,无论你需要多少帧。

这使得分类成本保持固定,场景变得越来越复杂,并且很容易调整,您只需将分类预算调整到您能负担的范围。

答案 2 :(得分:1)

我建议在这里借用其他一些解决方案。当然,我们从初始化时的完整对象开始。

我要做的就是每帧执行10次线性时间运行(如果发现对象已经完全排序,则提前终止)。例如,每次运行可以是bubble sort的一次通过,在整个阵列上有shell sort式的间隙:对于从0到n-gap-1的所有i,比较A [i]和A [ i + gap],如果没有排序则交换它们。您可以使用固定的间隙序列,或者更好,让它在帧之间变化;无论哪种方式,如果你做了足够多的框架,对象没有改变,你将有一个完全排序的序列。您甚至可以混合使用不同类型的子算法来进行运行,只要每次迭代都能提高“排序性”。

你可以添加Rafael Baptista的想法,通过在前段进行一次额外的跑步,或者选择将前半部分的间隙除以2,或类似的东西,轻松优先考虑场景的前方。

答案 3 :(得分:0)

它并没有像你想象的那样整洁地运作,因为你所要做的就是将相机旋转90度,并且排序的基础完全在不同的轴上。 (例如,X轴和Y轴是独立的 - 向下看X轴将导致排序顺序不依赖于X轴,向下看Y轴将导致排序顺序不依赖于Y轴。)即使是5度转弯也会造成很远的距离并且关闭#34; (就Z顺序而言)突然发生的事情"远"。

老实说 - 生成对象的绘制调用通常需要花费比分类它们更多的时间,特别是如果你的场景有一个优化的排序算法,你的游戏具有现代视觉复杂性

排序实际上可以是O(n),尤其是基于直方图的算法或基数型算法。 (是的,基数排序适用于整数,所以你必须将你的世界坐标缩放到整数,但除非你有一个巨大的世界,否则通常情况就足够了。)

话虽这么说,既然你已经在做O(n)操作了你正在绘制的所有内容,那么每帧使用并不会成为一个巨大的问题,特别是对于高低两者而言等级优化。

解决此问题的另一种常见方法是使用scene graph,但就您的目的而言,它最终基本上是每帧重新排序。但是,您可以在场景图遍历中构建视锥体剔除,阴影剔除和细节级别计算。

如果您正在寻找近似值,而不是进行z距离排序,请执行真正的距离排序并更频繁地更新排序顺序,以便更靠近对象,而不是更频繁地更新对象(取决于相机的距离)旅行)。这可以起作用,因为如果你离一个物体更远,移动不会导致观察者的角度经常变化,这反过来又意味着旧的分类数据更有可能是有效的。我并不喜欢这个,因为我喜欢算法,这些算法可以让我的游戏在地图上传送而没有任何问题。 (请注意,从磁盘流式传输资产成为传送的真正问题。)

答案 4 :(得分:0)

Shell sort适用于具有很少唯一值的列表以及“需要短代码而不使用调用堆栈”的一些场景。

在您的情况下,您需要一个名为Adaptive sort的东西,这意味着算法“利用其输入中的现有顺序”。

如果你的空间很小,你可以使用Straight Insertion Sort,它是自适应的。

否则你可以像@RunningWild建议的那样尝试Timsort和Smoothsort,它们都是自适应排序算法。