行进立方体效率 - 您可以减少3/4的边缘计算?

时间:2013-04-17 09:00:41

标签: algorithm marching-cubes

正常行进立方体每个立方体找到12条边,但每个立方体可以做3条边,将边保存在数组内,然后再次遍历立方体,引用相邻立方体的边而不是计算它们。

在互联网上没有清楚地讨论引用相邻立方体的过程,因此欢迎使用行进立方体的任何人帮助查找解决方案的详细信息。你知道一个实现吗?

这是一张图片,显示了每个立方体所需的黄色3条边,而不是12条。

Image

编辑 - 我刚刚找到了这个解决方案,虽然它只是其中的一部分:

想象一下,3个边缘来自立方体的角落,具有较低的坐标。然后所有其他边缘都属于其他立方体。如果我们的立方体具有坐标(x,y,z),则立方体立方体具有坐标(x + 1,y,z),(x,y + 1,z),(x,y,z + 1),(x + 1,y + 1,z),(x + 1,y,z + 1),(x,y + 1,z + 1)。您可以将边缘想象为矢量。然后立方体的角有边(1,0,0),(0,1,0),(0,0,1)。坐标为(x + 1,y,z)的立方体具有属于我们立方体的边(0,1,0)和(0,0,1)。立方体(x + 1,y + 1,z)只有一条边(0,0,1)属于我们的立方体。因此,如果您为多维数据集存储4个元素,则可以按以下方式访问它们:

edge1 = cube[x][y][z][0];
edge2 = cube[x][y][z][1];
edge3 = cube[x][y][z][2];
edge4 = cube[x+1][y][z][1];
edge5 = cube[x+1][y][z][2];
edge6 = cube[x][y+1][z][0];
edge7 = cube[x][y+1][z][2];
edge8 = cube[x][y][z+1][0];
edge9 = cube[x][y][z+1][1];
edge10 = cube[x+1][y+1][z][2];
edge11 = cube[x+1][y][z+1][1];
edge12 = cube[x][y+1][z+1][0];

现在哪个指向edge7连接?答案是(x,y + 1,z)和(x,y + 1,z)+(0,0,1)=(x,y + 1,z + 1)。

现在哪个立方体edge7连接?这更难。我们看到坐标z沿边缘变化,这意味着neibour立方体具有相同的z坐标。现在所有其他坐标都会改变在我们有+1的情况下,立方体具有大的坐标。在我们有+0的情况下,立方体的坐标较小。因此边连接立方体(x,y,z)和(x-1,y + 1,z)。其他具有相同边的2个立方体是(x,y + 1,z)和(x-1,y,z)。

- = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - =

EDIT2- 所以我这样做,并不是那么简单。我有一个循环,它同时在一个循环中计算8个点,12个边,边的插值,位值和边的值的顶点。

所以我在它之前做一个新的循环来尽可能地计算并将它放在数组中以用于复杂的循环。

我可以在数组中循环沿着边缘的交点的插值,尽管我将不得不在复杂的循环中再次重新计算所有点,因为我用来决定引用值的位数的点的值在顶点表中。这让我很困惑!我认为,一旦我有了边缘交点值,我就可以直接使用它们来获取三角形表,而不必再次计算点数!

事实上没有。 无论如何,如果只有它是可读的,这里有另外一些已经做过的人的信息! http://www.new-npac.org/projects/sv2all/sv2/vtk/patented/vtkImageMarchingCubes.cxx 滚动到这一行:立方体负责其最小面上的边缘。

3 个答案:

答案 0 :(得分:2)

以您建议的方式减少边缘计算的一种简单方法是一次计算一个轴对齐平面的立方体。

如果将所有立方体及其边缘保留在内存中,则只需计算一次边缘并通过索引查找相邻边缘就很容易了。但是,由于空间要求,您通常不希望立即将所有多维数据集保留在内存中。

对此的解决方案是一次计算一个立方体平面。即一个轴对齐的横截面,从一侧开始并前进到另一侧。然后,您一次只需要在内存中保留最多两个完整的立方体平面。当您在每个平面中移动时,您可以引用前一个平面中的共享边以及当前平面中先前计算的多维数据集。当您移动到下一个平面时,您可以释放不再需要的平面。

修改:本文讨论了我的建议: http://alphanew.net/index.php?section=articles&site=marchoptim&lang=eng

答案 1 :(得分:1)

很有趣,因为当我实现自己的MC时,我想出了类似的解决方案。

当你开始使用MC时,你将它们视为一个独特的立方体,但如果你想要获得高性能,你需要创建整个网格作为一个整体,创建顶点索引等并不是那么容易。当你想要添加平滑的每顶点法线时,它会变得更有趣:)。

为了解决这个问题,我创建了一个简单的索引缓存机制来存储每个边的顶点索引。 然后,对于每个计算的边缘,我有立方体位置x,y,z和边缘索引,我这样做:

For each axis:
    if the edge is on '+' side of axis:
        replace edge index with its '-' side sibling
        increment cube position along axis

这个简单的操作为我提供了正确的立方体位置,并且边缘索引为0,1,2。然后我使用简单的位旋转从x,y,z,edgeIndex值计算总缓存索引。

当我有缓存索引时,我会检查它是否大于 -1 。如果是,那么在这个边缘有一个已经计算过的顶点,我可以重复使用它。如果它是 -1 我需要创建一个新的顶点并将其索引存储在缓存中。这样,您只需计算一次每个顶点,甚至可以添加包含顶点的每个三角形之间共享的正常值。

答案 2 :(得分:1)

是的,我认为我的做法与kolenda类似。我有一个具有5个整数的结构:(多维数据集)索引和4个顶点索引(A,B,C,D)。

对于最内层的循环(x),我只有lastXCache和nextXCache。在指向-x方向的4个边上,我询问lastXCache.A!= -1,如果是,则分配先前计算的值,依此类推。 在+ x方向上,我将计算的顶点存储在nextXCache中。完成多维数据集时:lastXCache = nextXCache; 对于y和z方向,它必须是一个列表(可变数组的统一术语),下一个y是下一行(所以sizex),下一个z是下一个平面(所以sizex * sizey)

唯一的缺点是,这种方式必须依次运行多维数据集,因此必须依次运行。但是您可以并行计算不同的块。

我想到的另一种可能更平行的方法是需要2次遍历:1.完成1个操作后,每个立方体计算3条边-> 2.绘制三角形。

真的不知道有什么更好的方法,但是它的实际运行方式似乎足够快。统一工作甚至更好。为1个块/网格创建一个IJob。