Octree

(by J. Eder 9225396)

What is an Octree?
 Using an Octree
 Types of Octrees
    What does it represent?
    How is it managed?
How to structure Octree-structures
Pointerless full Octree
Traditional design of Pointer Octrees
Branch-On-Need-Octree

What is an Octree ?

(First I have to say, that it was discussed, if an Octree which doesn't branchs in all directions in the same moment is not an Octree: this paper doesn't matter when to branch, it just assumes that you have not less than zero and not more than eight child-nodes and in any meaning its matter is volumes)
Because many volume data-sets are very large it isn’t possible to interactively render such a scene. The implementation of hierarchical data structures could make this better. An Octree is such a hierarchical data structure.
It is a kind of a tree, which nodes (except the leaves) each has max. eight child-nodes. Mostly the nodes represent volume-pieces which are further divided into max. eight “Octants” which are represented by the child-nodes. The Octants mostly are regular, but also irregular shapes are imaginable. If the maximum resolution is reached at one node, it’s a leave of the tree. If the leaves of a node have exactly the same attributes, it is possible to reduce them to a node. This is called condensation.

Using an octree

Usually an Octree represents a volume, but of course it is possible to use it for different applications, such as to represent spatial relationships of geometrical objects. But the most common use is to represent a volume. Some calculations work very much faster if they can be performed on condensed node. Of course each node "carries" a type of information in it. If its job is just to represent if the "voxel" is there or not it's a boolean data type. On the other hand it is arguable to store a complete set of values, for instance dense values, temperature values and the further. This data type is non boolean.

Type of Octrees

There are several methods to distinguish the "family" of octrees, but I would say the best are:

What does it represent?
 How is it managed?

What does it represent?

There you can distinguish two mainly different types of information: Boolean and Non-Boolean Information. At the boolean information there's just the information saved, if the volume is interesting (BLACK) or not (WHITE). This binary decision only works clear for leaves, but for any ancestor-nodes there can be a third possibility: Some of the nodes could be BLACK and some of the nodes could be WHITE, then these ancestor-nodes are called "GRAY".
With Non-Boolean data it's not so easy: The information can be any value and its the more unlikely that you can condense the nodes than the more possibilities the attributes of the nodes have to get set. Therefore mostly all ancestor-nodes are gray nodes, which means that cannot be condensed and for nearly all discrete volume points you need its ancestors, which need also memory. This can be enhanced until about the double amount.

How is it managed?

There are any positibilities to implement an octree, the mostly common approaches are pointer based and with array (pointerless). The pointerbased octrees have for each child node a pointer which shows the position of the child. Sometimes there is also a pointer to the ancestor. (Of course not to forget the attributes of the node) If a gray node is defined it means, that all pointers are filled and this of course takes memory. The next approach is the linear octree which works (nearly) without pointers. If you decide, always to make a "full" octree (all ancestors have all child-nodes), you exactly know for a given size of a volume the amount of nodes in one hierarchical plane. Thats why it's directly possible to calculate the address of the wanted node. This method is very memory effortable, because it assumes a full octree.
If the path is saved, how to get from the root to the leaf,  it needs not a full octree. Each node now possess' a key which describes to take which turn-off. These keys can be accessed through an array or a Hash-table.

How to structure Octree-structures

If you are working with trees it's hard enough to remember the correct pointer for the wanted child and not to accidentally exchange it. But with octrees is really tricky. That's why we should consider a name-giving convention. It's called the ZYX-convention. In this convention you take the binary number of 0-7 to describe the nodes. Then you say: first bit=Z-Coordinate, second bit is Y-Coordinate, third bit is X-Coordinate. For each of these coordinates you say "less" half or "greater" half (in coordinate direction) and for less you say 0 and for greater you say 1.

Pointerless full Octree

In a full Octree each node has exactly eight children for each ancestor. Therefore you can directly calculate the total number of nodes.

It is a regular structure and therefore the adress of the node can be directly calculated. It cost much memory ( for 320x320x40 you need about 40 Mill words) and therefore the traditional design is with pointers.

Traditional design of Pointer Octrees

Each ancestor-node has eight pointers which can be empty or can point to the child-node. It works with the "even-subdivision strategy". This means, that each range is devided in nearly similar parts and the child nodes nearly exactly describe the same volume. But the disadvantage is described below with an example of 16x8x4 :

X Y Z
16 8 4
8,8 4,4 L,L
4,4,4,4 L,L,L,L
L,L,L,L,L,L,L,L

The tree is branching seperated in all dimensions which means that the lowest level the leaves (the "L"s) are not efficient because all have much Y and Z informations equal to another. Additionally they are the most.

Branch-on-Need Octree

The basic idea is, that the tree is only branching if "necessary" trying branching similar to the power of 2. This is realized by dividing at that moment at the binary number of the range is losing its leftmost bit. That works like following: You write down the binary numbers of the ranges which are the differences of their "upper" and their "lower" limits. Then you write all leading zeros you need that you have the same number of digits of all dimensions. You now only branch in that dimensions which have a "1" in their leftmost bit. For example you have (16/15/8) or (10000/01111/01000): It is branching only in the X direction. You can determin the childs ranges just for the "upper" by simply removing the leftmost 1. It results in 0000, which means "no further branch". The "lower" child you just take as "1"s in the number of the number of digits of the remaining "upper" range. The "lower" child has 1111 which means it now is a "power"-node which means it branches in that dimension down for all nodes (like the power of 2: 15-7-3-1).

 (Source: ACM Transactions on Graphical, Vol.11 No3,July '92)
//------------------------

OCTREE 教程

 
 
对OCTREE的描述

  OCTREE 是对3D空间进行划分,也可以叫空间分割。他允许你只对你的3D世界中摄象机照射的区域进行作画。他也能用于冲突检测。下面讲一下为什么要进行空间分割。假设你建立了一个游戏世界,这个世界有超过100,000个多边形要画。如果你建立一个循环并传递这些多边形,那速度是很慢的。即使你有一块很好的显示卡,他也会有很大的麻烦。但是玩你游戏的玩家的显示卡不会超过300$。有没有一种方法只渲染摄象机看见的多边形?那就是美丽的OCTREE。他允许你快速的找到你要渲染的多边形。

OCTREE是怎样工作的

  OCTREE工作在立方体中。最初,OCTREE从根接点开始,这个根接点对齐于立方体中整个世界,水平或场景的axis中心线。因此,把我们的整个世界想象成一个不可见的立方体。
 

  现在根接点存储了世界中的全部顶点。这是,他还是不能给我们任何好处,因为他还会画所有的东西。我们想把这个接点分成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

Ben Humphrey (DigiBen)
Game Programmer
DigiBen@GameTutorials.com
Co-Web Host of http://www.gametutorials.com/

Woo算法

候选平面的方向朝前,用粗线
条表示,与射线之间的交点用
灰色表示。由于左边的相交点
距离射线原点最远,因此将该
点作为潜在相交点。

针对射线与AABB之间的相交检测,Woo提出了一些巧妙的优化
方法。
基本思想:在组成AABB的6个平面中选出3个候选平面。对于每
一对互相平行的平面来说,忽略其背向平面而不作进一步考虑。
计算射线与候选平面的相交距离(t值),其中的最大值就可能对应
一个相交情形。如果找到潜在的相交情形(相交点位于AABB的相
应面上) ,再计算出实际的相交点。

Intesection intersect( const Ray &one, const AxisAlignedBox &two )//Ray用参数表示则在0,1之间

{
    OctreeSceneManager::intersect_call++;
    // Null box?
    if (two.isNull()) return OUTSIDE;
 // Infinite box?
 if (two.isInfinite()) return INTERSECT;

bool inside = true;
    const Vector3& twoMin = two.getMinimum();
    const Vector3& twoMax = two.getMaximum();
    Vector3 origin = one.getOrigin();
    Vector3 dir = one.getDirection();

Vector3 maxT(-1, -1, -1);

int i = 0;
    for(i=0; i<3; i++ )
    {
        if( origin[i] < twoMin[i] )
        {
            inside = false;
            if( dir[i] > 0 )
            {
                maxT[i] = (twoMin[i] - origin[i])/ dir[i];//循环一次,求出与三个平面相交的参数(0, 1)
            }
        }
        else if( origin[i] > twoMax[i] )
        {
            inside = false;
            if( dir[i] < 0 )
            {
                maxT[i] = (twoMax[i] - origin[i]) / dir[i];
            }
        }
    }//--------------------------以上说明原点在包围盒内部,origin大于最小的点,小于最大的点

//参数为0--1

return INTERSECT;

if( inside )
    {
        return INTERSECT;
    }
    int whichPlane = 0;
    if( maxT[1] > maxT[whichPlane])
        whichPlane = 1;
    if( maxT[2] > maxT[whichPlane])
        whichPlane = 2;                                 //求出最大的那个参数,就是潜在的求交平面

if( ((int)maxT[whichPlane]) & 0x80000000 )
    {
        return OUTSIDE;
    }
    for(i=0; i<3; i++ )
    {
        if( i!= whichPlane )
        {
            float f = origin[i] + maxT[whichPlane] * dir[i];
            if ( f < (twoMin[i] - 0.00001f) ||                  //如果小于最小值或大于最大值,则不向交
                    f > (twoMax[i] +0.00001f ) )
            {
                return OUTSIDE;
            }
        }
    }

return INTERSECT;

}

Intersection intersect( const AxisAlignedBox &one, const AxisAlignedBox &two )
{
    OctreeSceneManager::intersect_call++;
    // Null box?
    if (one.isNull() || two.isNull()) return OUTSIDE;
 if (one.isInfinite()) return INSIDE;
 if (two.isInfinite()) return INTERSECT;

const Vector3& insideMin = two.getMinimum();
    const Vector3& insideMax = two.getMaximum();

const Vector3& outsideMin = one.getMinimum();
    const Vector3& outsideMax = one.getMaximum();

if (    insideMax.x < outsideMin.x ||
            insideMax.y < outsideMin.y ||
            insideMax.z < outsideMin.z ||
            insideMin.x > outsideMax.x ||
            insideMin.y > outsideMax.y ||
            insideMin.z > outsideMax.z )//最大值小于最小值,一定在外面
    {
        return OUTSIDE;
    }

bool full = ( insideMin.x > outsideMin.x &&//内部最小的大于外部最小的,内部最大的小于外部最大的
                  insideMin.y > outsideMin.y &&
                  insideMin.z > outsideMin.z &&
                  insideMax.x < outsideMax.x &&
                  insideMax.y < outsideMax.y &&
                  insideMax.z < outsideMax.z );

if ( full )
        return INSIDE;
    else
        return INTERSECT;

}

http://docs.google.com/viewer?a=v&q=cache:z8AWOjhGRAgJ:www.cad.zju.edu.cn/home/jin/course/chapter13.pdf+Woo+%E7%9B%B8%E4%BA%A4&hl=zh-CN&pid=bl&srcid=ADGEESih9zGwXYJIDhtbbrphcFuqTi18DK59zpcCT00MXhJLJPRy9DuIuzYYRpId42XIQz9nOxE9BfC8j2KzCPzu3ssof33BRsH2S0CfHu8vQFkmFbZwDVj4jeujxxdN1rExBLpNlVQG&sig=AHIEtbTq1uZ1bzcCQHN88u0wmEBcs6HrVQ

Intersection intersect( const Sphere &one, const AxisAlignedBox &two )
{
    OctreeSceneManager::intersect_call++;
    // Null box?
    if (two.isNull()) return OUTSIDE;
 if (two.isInfinite()) return INTERSECT;

float sradius = one.getRadius();

sradius *= sradius;

Vector3 scenter = one.getCenter();

const Vector3& twoMin = two.getMinimum();
    const Vector3& twoMax = two.getMaximum();

float s, d = 0;

Vector3 mndistance = ( twoMin - scenter );
    Vector3 mxdistance = ( twoMax - scenter );

if ( mndistance.squaredLength() < sradius &&//最大点及最小点到球心的距离均小于半径,则在包围盒内部
            mxdistance.squaredLength() < sradius )
    {
        return INSIDE;
    }

//find the square of the distance
    //from the sphere to the box
    for ( int i = 0 ; i < 3 ; i++ )                        //在最小点的左边,并且到最小点的距离小于半径
    {
        if ( scenter[ i ] < twoMin[ i ] )
        {
            s = scenter[ i ] - twoMin[ i ];             //在最大点的右边,并且到最大点的距离大于半径
            d += s * s;
        }

else if ( scenter[ i ] > twoMax[ i ] )
        {
            s = scenter[ i ] - twoMax[ i ];
            d += s * s;
        }

}

bool partial = ( d <= sradius );

if ( !partial )
    {
        return OUTSIDE;
    }

else
    {
        return INTERSECT;
    }

}

转载于:https://www.cnblogs.com/lizhengjin/archive/2010/10/08/1846111.html

关于八叉树及三维场景管理算法相关推荐

  1. 算法与数据结构java语言描述 英文版_CVPR2020 |室内设计师失业?针对语言描述的自动三维场景设计算法...

    近日,计算机视觉顶会CVPR 2020接收论文结果公布,从6656篇有效投稿中录取了1470篇论文,录取率约为22%.在<Intelligent Home 3D: Automatic 3D-Ho ...

  2. 3D游戏引擎中常见的三维场景管理方法

    对于一个有很多物体的3D场景来说,渲染这个场景最简单的方式就是用一个List将这些物体进行存储,并送入GPU进行渲染.当然,这种做法在效率上来说是相当低下的,因为真正需要渲染的物体应该是视椎体内的物体 ...

  3. 线性八叉树_游戏场景管理的八叉树算法是怎样的?

    最近正好在看吧.就谈一下数据结构. 八叉树特别适合进行空间划分.在需要邻近点信息做参考时,利用八叉树做搜索会非常高效. 八叉树必备的元素:尺寸:一般就是自己限定的空间(有的地方也叫bounding b ...

  4. 3D游戏场景管理概述

    参考文章 http://www.cnblogs.com/kex1n/archive/2012/08/26/2657054.html 关于场景管理概述 http://www.cnblogs.com/wa ...

  5. 3D游戏引擎入门课程——场景管理

    目录 写在前面 场景管理 场景管理概述与BVH 场景组织结构:场景树,八叉树和BSP树 场景树 八叉树 BSP树 场景管理实例 OGRE场景管理 OSG场景管理 Panda3D场景管理 写在前面 本专 ...

  6. 博微三维技术篇【四】——大规模、高精度三维场景渲染

    三维场景渲染技术 三维场景渲染是计算机从三维场景内获取模型.材质和光照等基本信息并通过复杂计算输出真实感高图像的过程.三维场景渲染是三维图形平台内最重要的模块之一,主要负责对图形的组织.管理与显示,实 ...

  7. 点云上的深度学习及其在三维场景理解中的应用————PointNet(一)

    最近在学3D方向的语义分析. 师兄推荐了一个哔哩大学的将门创投 | 斯坦福大学在读博士生祁芮中台:点云上的深度学习及其在三维场景理解中的应用!的宝藏视频,我会多看几遍,并写下每次观看笔记. 下文的截图 ...

  8. 游戏场景管理(四)遮挡剔除

    1.画家算法 早期的gpu是没有z-buffer的,为了得到正确的图像,必须使用画家算法,也就是从后往前绘制几何体.几何体每帧都需要根据摄像机的位置进行排序,进而实现从后往前的绘制.画家算法不仅低效, ...

  9. 游戏场景管理—四叉树

    内容会持续更新,有错误的地方欢迎指正,谢谢! 场景管理:把不同的物体归属到不同类别里,而相似性的判断是根据物体的空间相干性. 把物体分类的目的:希望下次能快速找到所需的物体,所以更偏向于用四叉树来管理 ...

最新文章

  1. ES transport client批量导入
  2. xp系统如何开启索引服务器,Windows XP系统关闭磁盘索引的两个方法图文教程
  3. LeetCode hard 668. Kth Smallest Number in Multiplication Table(二分答案)
  4. 我的2021年度总结
  5. C#使用了未赋值的局部变量
  6. @Async join
  7. CAD(计算机辅助设计)
  8. pip设置国内镜像_virtualenv安装、使用、pip国内镜像替换---windows 0117-2020
  9. 有趣的算法(七):3分钟看懂希尔排序(C语言实现)
  10. Affinity Publisher for Mac(桌面排版神器)中文版
  11. 优质编程网站推荐(适合学习和查资料)
  12. 《数学之友》期刊简介及投稿要求
  13. 简单的魔方复原方法, 魔方还原公式,图解
  14. [生产力]必备的全局文件搜索工具
  15. Bootstrap3 按钮状态切换
  16. linux bios 禁用usb设备,当USB在UEFI / BIOS中工作时,为什么USB在Linux中不工作?
  17. OK6410A 开发板 (三) 4 u-boot-2021.01 boot 解析 SPL 编译链接部分
  18. sdi线缆标准_常用线缆传输距离的汇总
  19. Hugegraph合集(一):图数据库技术调研
  20. 阿克曼函数java代码_阿克曼函数

热门文章

  1. linux 查找文件locate,Linux locate命令:按照文件名搜索文件
  2. 重磅!特斯拉FSD软件版本“回滚”:突破还是妥协
  3. 战舰猎手服务器不稳定,战舰猎手进阶技巧 最大限度走位 杀敌逃跑两不误
  4. java cmd获取端口号_java----cmd命令
  5. CF中User-Based与Item-Based的区别
  6. 偶像工场,开启虚拟人数字藏品新时代
  7. 一文搞懂结构体内存对齐
  8. mkfs.minix.c之minix_super_block.s_ninodes获取解析
  9. 双系统用Gparted调整Ubuntu 18.4分区
  10. cv2.rectangle不会修改原图像