现在我在家终于可以回答你的问题了,所以让我们开始......
递归
首先:递归总是比迭代慢,如果您正在使用结构化编程语言。您不能概括这一点,因为函数式编程语言中的函数调用速度更快(函数的定义不同)。了解更多信息维基百科 http://en.wikipedia.org/wiki/Recursion_%28computer_science%29#Recursion_versus_iteration是一个很好的来源。
详细来说,递归调用将函数(或方法)分配的所有局部变量压入堆栈,等待内部调用返回(这包括不断重复的相同过程...),最后从堆栈中弹出值调用堆栈并继续与他们合作。这不仅是沉重的内存负载,对于垃圾收集器来说也是痛苦的:函数必须等待的时间越长,对象在内存中停留、老化和最终到达的时间就越长gen1 or gen2。这意味着他们实际上需要很长时间才能被释放。
我看到的另一个问题如下:
public void LineBoxer(int[] check)
{
// ...
LineBoxer(octmanB.node[check[i]].child);
// ...
}
递归传递数组意味着all数组的值在堆栈上驻留了很长时间。即使大部分元素已经准备好发布!
如果你迭代地做同样的事情,那么堆栈就不会有压力。分配的变量通常是临时变量,并且可以很快释放。内存分配是这里的瓶颈。这(并且因为您在评论中要求这样做)就是为什么我将继续更详细地介绍。
提高性能——理论上
然而(在评论中)您还询问如何更有效地处理光线追踪。基本上你使用八叉树是正确的。您要执行的基本操作是简单的搜索。这就是你的问题。据我了解您的代码,您正在测试每个八叉树叶子是否被击中:
public void LineBoxer(int[] check) // Cast a ray against boxes
{
for (int i = 0; i < check.Length; i++)
{
// ...
}
}
这只是简单地搜索所有与光线相交的盒子。但这并不是引入树木的动机。你可以想象一个八叉树二叉搜索树 http://en.wikipedia.org/wiki/Binary_Tree扩展到 3 维。二叉搜索树可以在一维中搜索条目(例如列表或数组)。要搜索二维结构中的信息,您可以使用四叉树 http://en.wikipedia.org/wiki/Quadtree。现在我们需要添加另一个维度(因为我们现在处于 3D 状态),所以我们使用octrees http://en.wikipedia.org/wiki/Octree.
到目前为止一切顺利,但是树如何帮助我们“更好”地执行搜索操作呢?
那是因为分而治之原则 http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm。如果我们要在更大的信息集中搜索特定的内容,我们会将信息集分成小块。我们这样做直到找到我们正在寻找的具体东西为止。
对于我们的八叉树来说,这意味着我们将一个立方体分为8更小的立方体。现在我们测试每个盒子是否与我们的光线相交。在最好的情况下,它恰好与一个盒子相交。这是需要进一步检查的问题。但如果每个盒子都包含1000我们只需扔掉盒子7000通过一张额外的支票进行检查!
现在我们一次又一次地这样做,直到找到一片或多片叶子。递归执行此操作不应比迭代执行此操作慢很多。想象一个有 100,000 个节点的八叉树。第一个立方体可以存储 8 个立方体,这 8 个立方体可以存储 64 个立方体(深度:2!)等等......
- 深度 = 3 : 512 个立方体
- 深度 = 4 : 4.096 立方体
- 深度 = 5 : 32.768 立方体
- 深度 = 6 : 262.144 立方体
- 深度 = 7 : 2.097.152 立方体
- ...
And our maximum number of checks is never more than 8 x Depth elements if we are searching for one specific box. So we increased our algorithm performance from O(n) to O(log(n)). 1
很好,但是如何将其应用于您的问题呢?
首先:您正在使用 C#——一种面向对象的语言。所以使用对象!如果您将所有内容都封装在对象结构中,那么将分而治之的原则应用于树结构是非常简单的。
在您的具体情况下,这意味着:
public class OctreeNode
{
public bool IsLeaf { get; private set; }
public OctreeNode[8] Children { get; private set; }
public OctreeNode()
{
this.IsLeaf = true;
this.Children = null;
}
// Don't forget to implement methods to build up the tree and split the node when required.
// Splitting results in a tree structure. Inside the split-method
// you need to allocate the Children-Array and set the IsLeaf-Property to false.
public bool Intersects(Line rayline, ref ICollection<OctreeNodes> Nodes)
{
Interval interval;
// If the current node does not intersect the ray, then we do not need to
// investigate further on from here.
if (!Rhino.Geometry.Intersect.Intersection.LineBox(rayline, this.GetBoundingBox(), 0, out interval))
{
return false;
}
// If this is a leaf (in which we are interested in) we add it to
// the nodes-collection.
if (this.IsLeaf)
{
Nodes.Add(this);
return true;
}
// Not a leaf, but our box intersects with the ray, so we need to investigate further.
for (int i = 0; i < 8; ++i)
{
// Recursive call here, but the result doesn't actually matter.
this.Children[i].Intersects(rayline, Nodes)
}
// The ray intersects with our current node.
return true;
}
}
这将为您带来所有魔力!它仅测试树,直到测试失败的深度,并继续直到拥有光线相交的所有叶子。您还可以按照“谁获得了最大的交集间隔”对它们进行排序,以便在流式传输它们时将内部对象置于更高的优先级,但由于我完全失败了该主题,现在我将继续:
您甚至可以通过应用并行性进一步加速该算法。这很容易使用TPL http://msdn.microsoft.com/en-us/library/vstudio/dd460717.aspx,这很简单:
Parallel.For<int> (0; 8; i =>
{
this.Children[i].Intersects(rayline, Nodes)
});
好吧,我想暂时就够了。我希望这对您和周围的更多人有帮助。 :)
Note:我不太熟悉rhino3d。也许有内置功能可以帮助您更好地解决问题!
Note 2:请原谅我,当我在所有观点上都不是 100% 正确时,我已经有一段时间没有进行这些理论上的考虑了......
1 In best case, where we are searching for one specific leaf in a completely balanced tree.