具有不同半径的圆覆盖算法

时间:2013-12-31 01:25:35

标签: algorithm math geometry

对于一个游戏,我绘制了几千个随机分布的圆形的密集簇,这些圆圈具有不同的半径,由一系列(x,y,r)三元组定义。这是一个包含14,000个圆圈的示例图像:

example image consisting of 14,000 circles

我有一些动态效果,例如合并群集,但为了实现这一点,我需要每帧重绘所有圈子。

所绘制的圆圈中有很多(可能是80-90%)被后续绘制所覆盖。因此我怀疑通过预处理,我可以通过消除覆盖的圆圈来显着加快我的绘制循环。是否有一种能够以合理的效率识别它们的算法?

我可以忍受相当多的假阴性(即绘制一些实际被覆盖的圆圈),只要它不是那么多,绘图效率会受到影响。我也可以容忍误报,只要它们几乎是正面的(例如删除一些仅覆盖99%的圆圈)。我也可以改变圈子的分布方式,只要看起来还不错。

5 个答案:

答案 0 :(得分:5)

这种剔除本质上是隐藏的表面算法所做的 - 特别是称为“对象空间”的变种。在您的情况下,圆的排序顺序为它们提供了有效的恒定深度坐标。它不变的事实简化了问题。

关于HSA的经典参考是here。我会给它一个读取的想法。

受这种想法启发的一个想法是用“扫描线”算法考虑每个圆圈,比如从上到下移动的水平线。扫描线包含它正在触摸的一组圆圈。通过按顶部坐标对圆的输入列表进行排序来初始化。

扫描在“事件”中前进,“事件”是每个圆圈的顶部和底部坐标。到达顶部时,将圆圈添加到扫描中。当它的底部出现时,将其移除(除非它已经如下所述消失)。当一个新的圆圈进入扫描时,请考虑它已经存在的圆圈。您可以将事件保存在最大(y坐标)堆中,根据需要随意添加它们:下一个输入圆的顶部坐标加上所有扫描线圆的底部坐标。

进入扫描的新圆圈可以完成3件事中的任何一件或全部。

  1. 扫描中的圆圈更深,更深。 (由于我们正在识别要绘制的圆圈而不是,因此该决定的保守方面是使用新圆圈中包含的最大轴对齐框(BIALB)来记录每个现有更深圆圈的模糊区域。)

  2. 被其他深度较小的圆圈遮挡。 (这里保守的方法是使用彼此相关圆的BIALB来记录新圆圈的模糊区域。)

  3. 有没有遮挡的区域。

  4. 必须保持每个圆圈的模糊区域(通常会在处理更多圆圈时增长),直到扫描线到达其底部。如果遮挡区域在任何时候覆盖整个圆圈,则可以删除它并且不会被绘制。

    模糊区域的记录越详细,算法就越好。矩形区域的联合是一种可能性(例如,参见Android的区域代码)。单个矩形是另一个,但这可能会导致许多误报。

    类似地,还需要用于在扫描线中找到可能模糊和模糊的圆的快速数据结构。包含BIALB的区间树可能很好。

    请注意,在实践中,像这样的算法只能产生原始数量的巨大成功,因为快速的图形硬件是如此......快。

答案 1 :(得分:3)

这是一个简单的算法:

  1. N个圆圈插入四叉树(首先是底部圆圈)
  2. 对于每个像素,使用四叉树确定最顶部的圆圈(如果存在)
  3. 使用圆圈颜色填充像素
  4. 通过添加圆圈,我的意思是将圆心添加到四叉树。这会为叶节点创建4个子节点。将圆存储在该叶节点(现在不再是叶子)中。因此,每个非叶节点对应一个圆圈。

    要确定最顶部的圆,请遍历四叉树,如果像素与该节点处的圆相交,则沿途测试每个节点。最顶部的圆是树中最深的一个与像素相交的圆。

    这应该花费O(M log N)时间(如果圆圈分布得很好),其中M是像素数,N是圆圈数。如果树退化,则更糟糕的情况仍为O(MN)

    伪代码:

    quadtree T
    for each circle c
        add(T,c)
    for each pixel p
        draw color of top_circle(T,p)
    
    def add(quadtree T, circle c)
        if leaf(T)
            append four children to T, split along center(c)
            T.circle = c
        else
            quadtree U = child of T containing center(c)
            add(U,c)
    
    def top_circle(quadtree T, pixel p)
        if not leaf(T)
            if intersects(T.circle, p)
                c = T.circle
            quadtree U = child of T containing p
            c = top_circle(U,p) if not null
        return c
    

答案 2 :(得分:3)

根据您提供的示例图像,您的圆圈似乎具有接近恒定的半径。如果它们的半径不能低于大量像素,则可以利用圆的简单几何来尝试图像空间方法。

想象一下,您将渲染曲面划分为正方形网格,以便最小的渲染圆可以像这样适合网格:

grid 1 grid 2

圆半径为sqrt(10)网格单位并且至少覆盖21个方格,因此如果您标记的方块完全与已绘制的任何圆重叠,则您将消除圆周表面的大约21 / 10pi分数,约为2/3。

您可以通过正方形here

获得最佳圆圈覆盖率的一些想法

剔除过程看起来有点像反向画家算法:

For each circle from closest to farthest
   if all squares overlapped (even partially) by the circle are painted
       eliminate the circle
   else
       paint the squares totally overlapped by the circle

你还可以通过绘制不完全被给定圆圈覆盖的网格方块(或者消除从已经绘制的表面略微溢出的圆圈)来“欺骗”,以一些误报为代价增加被消除的圆圈的数量。

然后,您可以使用Z缓冲区算法渲染剩余的圆圈(即让GPU完成剩余的工作)。

基于CPU的方法

这假设您在没有GPU帮助的情况下将网格实现为内存位图。

要确定要绘制的方块,可以使用基于圆心相对于网格的距离(示例图像中的红色十字)和实际圆半径的预计算模式。

如果直径的相对变化足够小,您可以定义一个由圆半径和中心距离最近网格点的距离索引的二维模式表。

一旦检索到正确的图案,就可以使用简单的对称将其应用到适当的位置。

可以使用相同的原理来检查圆是否适合已经涂漆的表面。

基于GPU的方法

自从我使用计算机图形以来已经很长时间了,但是如果当前的技术水平允许,你可以让GPU为你做绘图。

绘制网格将通过渲染每个圆圈以适合网格

来实现

检查消除需要读取包含圆的所有像素的值(缩放到网格尺寸)。

<强>效率

网格维度应该有一些最佳点。更密集的网格将覆盖更高百分比的圆形表面,从而消除更多圆圈(更少的漏报),但计算成本将增长为o(1 /grid_step²)。

当然,如果渲染的圆圈缩小到大约1个像素的直径,你也可以转储整个算法并让GPU完成工作。但与基于GPU像素的方法相比,效率随着网格步长的平方而增长。

在我的示例中使用网格,对于一组完全随机的圆圈,你可能会得到大约1/3的假阴性。

对于你的图片,似乎定义了卷,前景圆的2/3和(几乎)所有后向圆应该被消除。剔除超过80%的圈子可能是值得的。

所有这些都说,在蛮力计算竞赛中击败GPU并不容易,所以我对你可以期待的实际性能增益只有最模糊的想法。不过可能会很有趣。

答案 3 :(得分:1)

如果圆圈完全位于另一个圆圈内,则必须遵循它们的中心之间的距离加上较小圆圈的半径最多为较大圆圈的半径(为自己画出来看看!)。因此,您可以查看:

float distanceBetweenCentres = sqrt((topCircle.centre.x - bottomCircle.centre.x) * (topCircle.centre.x - bottomCircle.centre.x) + (topCircle.centre.y - bottomCircle.centre.y) * (topCircle.centre.y - bottomCircle.centre.y));

if((bottomCircle.radius + distanceBetweenCentres) <= topCircle.radius){
  // The bottom circle is covered by the top circle.
}

为了提高计算速度,您可以先检查顶部圆圈的半径是否大于底部圆圈,就好像它没有,它不可能覆盖底部圆圈。希望有所帮助!

答案 4 :(得分:0)

你没有提到Z组件,所以我假设它们在你的列表中是Z顺序并且是从前到后绘制的(即画家算法)。

正如之前的海报所说,这是一次遮挡剔除练习。

除了提到的对象空间算法之外,我还研究了屏幕空间算法,例如Hierarchical Z-Buffer。你甚至不需要z值,只需要指示是否存在某些内容的位标志。

请参阅:http://www.gamasutra.com/view/feature/131801/occlusion_culling_algorithms.php?print=1

相关问题