现在根接点存储了世界中的全部顶点。这是,他还是不能给我们任何好处,因为他还会画所有的东西。我们想把这个接点分成8个部分。一次,我们划分时,有8个立方体包含那最初的根接点的立方体。那意味着有4个立方体在上面,4个立方体在下面。请看下图:
记住图中黄色轮廓线没有。
我们刚刚把这个世界分成8个部分。想象一下如果我们有2,3,4个部分,我们的世界将是怎样?我们的摄象机在世界的中间,向着后面靠右的角落。如果你看这些线,你将注意到在OCTREE中8个结点中的第4个。这些结点包含2个后面的顶和底结点。这意味着我们只用画存储在这些结点中的顶点。
我们如何检测出我们要画的结点?如果你学过破片拣选(frustum culling),这将是很简单的。你能得到这些破片的大小并检测每个结点,看他是否被截断或在你的视觉破片中。有一个关于破片拣选的教程在http://www.gametutorials.com. 如果一个立方体截断破片,我们就将画这些结点。在这个例子中,我们切的数量是我们需要画的50%。记住,这只是我们世界中的一个部分。部分越多,我们就将越精确。当然,我们不会需要太多的点。下面,我们来看一下下面的图:
在上面这幅图中,你将发现许多的不同。这个例子中,不是在原始的8个立方体的每一个结点中建立8个新的立方体。原始的8个结点的顶和底面没有细分。你总是把一个结点分成8个或更多的结点,但是,如果在这个面没有三角形可以存储,我们将忽视这个结点并不给他分配内存。如果我们进一步的细分,更多的结点将形成原始的世界。如果我们变换到另一个部分,那立方体将相似的变换。为了说明这一点,请看图:
在图中,有两个球,但是方向相反。注意左边的部分,他将那世界分成2个结点,不是8个。这是因为球只要2个结点。请看右边的球是不是和左边的很相似。这幅图告诉我们:只显示需要的部分结点。如果没有三角形占用空间,结点将不会建立。
何时停止细分
现在,我们明白了如何细分,但是我们也需要知道怎样停止细分。有许多的方法:
1。如果当时的三角形数量少于我们定义的最大的三角形数量,我们可以停止细分当前的结点。举个例子,我们定义三角形的最大数量为100。这意味着我们在细分结点之前,要检测一下当前三角形的总数量是否小于或等于我们定义的最大数量,然后再做决定。如果数量少于或等于,我们将停止细分并指定这些三角形到那个结点。请注意我们从不指定任何三角形到任何结点,除非他是末结点。如果我们划分一个结点,我们不能存储三角形到那个结点,但是可以存储在他的子结点或他们的子结点中,甚至到他们的子中,等等。当我们复习了怎样画0C树后,将会有更多的了解。
2。另一个方法是在停止细分时,看我们是否细分的数目是否超过了细分的标准。例如,我们可以先建立细分的标准为10,如果细分的数量大于这个标准,我们将停止并指定这些在立方体中这个面的顶点到那个结点。当我们说“大于这个标准”就意味着细分的数量有11个。
3。最后的方法是看结点数是否超过结点变量定义时的值。例如,我们设置结点变量的值为500。每次,我们建立一个结点,相应的结点数增加1。然后在我们每次建立另一个结点时,检测一下我们当前的结点数是否超过结点变量。如果结点数为501,我们将不会细分这个结点,但是指定他的当前顶点给他。我自己用1和3的方法,但是1和2也很好,因为你可以测试不同的细分的标准。
怎样画OCTREE
OCTREE建立后,我们就可以画我们需要的结点。那些立方体不是全部包含在我们的视觉中的,只有一部分。这就是我们为什么要计算每个结点的三角形的数量,如果我们只需要一个结点中的一部分,这样我们就不需要画成千上万个三角形。我们从根目录开始画OCTREE。对于每个结点都存储着一个中心点。这是非常好的,比如在下面这个函数中:
//这个函数取走立方体(X,Y,Z)的中心点和他的大小(width/2)
bool CubeInFrustum(float x,float y,float z,float size);
这个函数返回true or false,是由立方体是否在破片中决定的。如果立方体在破片中,我们将检测所有他的结点,看他们是否在破片中,否则我们将忽约树中的整个分枝。当我们得到了破片中的结点,但是没有任何结点在他下面。我们想画的顶点存储在末结点中。记住,只有末结点有顶点存储。看下面的图:
有阴影的立方体是在破片中。白色的立方体不是在破片中。这里显示了细分的2层。
OCTREE 中的冲突
OCTREE 不是用于渲染,但是用于冲突检测很好。随着游戏的发展,冲突检测也在改变,你将不得不在游戏世界中运用自己的算式检测你的角色或目标是否冲突。下面是一些冲突检测的例子:
1。 你将建立一个函数允许你转递3D空间中的点到你的OCTREE并返回在这个点周围的顶点。你将转递的那个点是你的角色和目标的中心点。这虽然可以工作一端时间,但如果这个点靠近一个立方体的边时呢?你的角色或目标可能和另一个立方体的顶点相冲突。为了解决这个问题,你将做一些事情。你可以转递角色/目标的半径或一定的空间,然后检测半径或一定的空间是否和周围的结点相冲突。这由你的角色/目标的形状决定。下面是一些典型的函数:
//这个函数取走角色/目标(X,Y,Z)的中心点并返回靠近他的顶点
CVector3 *GetVerticesFromPoint(float x,float y,float z);
//这个函数取走角色/目标(X,Y,Z)的中心点和半径,然后返回和他冲突的结点中的顶点
CVector3 *GetVerticesFromPointAndRadius(float x,float y,float z,float radius);
//这个函数取走角色/目标(X,Y,Z)的中心点和立方体的大小,然后返回和他冲突的结点中的顶点
CVector3 *GetVerticesFromPointAndCube(float x,float y,float z,float size);
我相信你有更好的方法快速的检测,在这里只是给你一点基础。
总结
这篇教程只是给你讲截如何建立自己的OCTREE。关于这篇文章中的代码在www.gametutorials.com,我希望这篇文章对你有用。
中文译者:antking
http://akinggame.gameres.com
这篇文章的英文版在http://www.gametutorials.com/Tutorials/OpenGL/Octree.htm