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)