想象一个N³分辨率的体素立方体,里面充满了遮挡体素。这个立方体可以完全填满,也可以包含弯曲的“隧道”、墙壁或只是几个杂散的体素;现在我们选择任意两个边界立方体的面,并尝试找到一条连接这两个面的线路,而不会碰到其中任何一个体素。如果存在这样的一条线路,则这些面可以相互看到,否则它们完全被遮挡。
我的问题是:是否存在O(n)(或更好)的算法来快速确定是否可以画出这样的线路?该线路的确切参数并不重要。
想象一个N³分辨率的体素立方体,里面充满了遮挡体素。这个立方体可以完全填满,也可以包含弯曲的“隧道”、墙壁或只是几个杂散的体素;现在我们选择任意两个边界立方体的面,并尝试找到一条连接这两个面的线路,而不会碰到其中任何一个体素。如果存在这样的一条线路,则这些面可以相互看到,否则它们完全被遮挡。
我的问题是:是否存在O(n)(或更好)的算法来快速确定是否可以画出这样的线路?该线路的确切参数并不重要。
对于这个(连续的?离散的?)空间中成为直线的确切参数,我有点不清楚,但我想知道你是否在寻找一种动态规划解决方案?
也许我们可以限制为二维左右的情况来构建算法,然后再进行概括:
循环遍历数组的第一列, 对于每个不透明的正方形,标记无法建立连接到该正方形的光线 对于每个非透明正方形,标记可以到达该正方形,并跟踪可到达该正方形的斜率范围。您可以通过可能到达体素体积另一端的斜率集来限制此初始化中的斜率集。
然后循环下一列
每个正方形可能被前一列中的任何正方形到达,但只有当可以到达前一列中的正方形的斜率范围与从前一列中的正方形到达当前正方形所需的斜率范围相交时才能到达当前正方形。
因此,您将当前正方形的有效斜率范围设置为先前正方形的有效范围与到当前正方形的有效范围的交集的联合。
继续循环列,直到达到远端,任何可到达远端的条目都将报告允许命中该正方形的向量斜率范围。
该算法的速度严重依赖于您如何快速地联合和交叉斜率范围(或在3D情况下,UV坐标的任意范围)。在2D连续空间中,可以通过排序来快速完成此操作,在3D离散空间中,可以使用一组可能的矢量斜率,在X、Y上依赖于您的体素空间的尺寸。在3D连续空间中,某种四叉树可能会接近2D排序所能实现的内容。MATRIX.MAX_Z * (
Time(MATRIX.GET_VOXEL(x,y,z))
+ Time(VOXEL.DRAW-LINE(0,0,0, 0,0,VOXEL_DEPTH))
)
算法
FUNCTION DRAW (INTEGER X, INTEGER Y)
INTEGER VOXEL_X = X / MATRIX.VOXEL_WIDTH
INTEGER VOXEL_Y = Y / MATRIX.VOXEL_HEIGHT
FOR i = 0 .. (MATRIX.MAX_Z-1)
VOXEL V = MATRIX.GET_VOXEL(VOXEL_X, VOXEL_Y, i)
INTEGER X_0 = X % MATRIX.VOXEL_WIDTH
INTEGER Y_0 = Y % MATRIX.VOXEL_HEIGHT
INTEGER Z_0 = 0
INTEGER X_1 = X_0
INTEGER Y_1 = Y_0
INTEGER Z_1 = (MATRIX.VOXEL_DEPTH-1)
V.DRAW-LINE(X_0,Y_0,Z_0, X_1,Y_1,Z_1)
END-FOR
END-FUNCTION
将问题分解为二维,很明显有些体素配置是无法从左到右穿透的:
+-+-+-+ +-+-+-+-+-+
| |#| | |#| | | | |
+-+-+-+ +-+-+-+-+-+
| |#| | |#| | |#| |
+-+-+-+ +-+-+-+-+-+
| |#| | |#| | |#| |
+-+-+-+ +-+-+-+-+-+
|#| | |#| |
+-+-+-+-+-+
| | | |#| |
+-+-+-+-+-+
...但这可能不是不可渗透的,这取决于您如何处理角落:
+-+-+-+-+-+
|#| | | |/|
+-+-+-+-+-+
|#| | |/| |
+-+-+-+-+-+
|#| |/|#| |
+-+-+-+-+-+
|#|/| |#| |
+-+-+-+-+-+
|/| | |#| |
+-+-+-+-+-+
... 这绝对是可能的:
+-+-+-+-+-+
|#| | | | |
+-+-+-+-+-+
|#| | | | |
+-+-+-+-+-+
|#| | | | |
+-+-+-+-+-+
| | | |#| |
+-+-+-+-+-+
| | | |#| |
+-+-+-+-+-+
现在,如果你能想到任何方法来区分上面的2D立方体和下面的立方体,那么可能会消除一些不可能的像素/体素配置 - 但我担心你需要测试目标侧的每个像素是否从任何角度透过光线从源侧穿过,这听起来非常像一个n平方问题(2D),或者在3D中是n的四次方。
在2D中,我会从左侧顶部开始,检查连接我的体素中心到右上角的线是否击中了遮挡像素:如果没有,我们就完成了;如果有,您需要调整角度,使射线通过遮挡物的左下角并继续检查,直到找到通道或到达右侧的末端。
继续处理源侧的每个像素,直到完成 - 无论如何都要这样做。
但这是蛮力法,我很想看到更优雅的解决方案,也许由G. Bach提供...?
因此,进行此测试的一种简单方法是以任意分辨率从源立方体渲染到目标立方体的视图(正交)。如果还剩下任何背景像素,则存在两个矩形之间的线。因此,复杂性归结为两件事:
现在对于二进制渲染,您唯一需要知道的是覆盖/未覆盖。这归结为两个八叉树,一个用于最小值,一个用于最大值。最小树跟踪“是否有任何打开的子节点(或)”,最大树跟踪“是否有任何关闭的子节点(和)”。构建这些树是n log(n),但查询仅为log(n)。
对于目标分辨率m,它应该只是log(m)。即使您将其提高到float大小的m = 2 ^ 23。