Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
2d positions = base 4 sequences (view plane locational codes); common elements = common prefixes. You can, with a single procedure, do view frustum culling, get the view homogeneous coordinates and project the visible points. All this without a single *, / or float.we use a mass 3d to 2d conversion that shares the common elements of the 2d positions of all the dots combined.
It differs from splatting/projection and ray casting more than they differ the one from the other. In fact, his description above is, it is believed, quite detailed.So how does this differ from algorithms of ~20 years ago that modelled objects with a multitude of spheres and then had a fast "blatting" algorithm?
/* Octree nodes are the buckets (internal nodes have <= 8 buckets: their children);
frustum octants are the things being sorted.
The hierarchy of squares is given by a 1 bit per node complete quadtree stored in level order (hence, father/children relationships are "computable").
The hierarchy of cubes is a sparse octree.
Call bucket_sort with cube = root node and stack = (viewport, view frustum) */
bucket_sort(cube, stack)
{
if (cube is a point) /* leaf node of our object octree */
do { /* there can be many frusta located at the same point, in a discrete world */
(square, frustum) = pop(stack)
if (!square.black) { /* a black square represents a completely filled screen region */
square.color = cube.color
do { /* update the occlusion pyramid/quadtree */
square.black = true
if (square is root)
return
square = square.father
} while (square[XY].black for all XY in { LU, RU, LD, RD }) /* while all the subsquares are black */
}
} while (stack)
else { /* the cube has subcubes */
do { /* distribute the given frusta (stack) among the cube's (non-empty) subcubes (the buckets); octosect the frusta as needed */
(square, frustum) = pop(stack)
if (!square.black) { /* skip occluded frusta */
if (frustum in cube[XYZ]) /* cube[XYZ] is a non-empty octant of cube; XYZ is e.g., "LUF" i.e., left-up-front */
push((square, frustum), stack[XYZ]) /* push on subcube "cube[XYZ]"'s stack */
else /* frustum in no octant of cube: octosect the frustum, quadrisect the square */
for (XY in { LU, RU, LD, RD }) /* LU: left-up quadrant, RU: right-up quadrant etc. */
if (!square[XY].black) /* if the XY quadrant/subsquare isn't black; doing this with a switch has benefits, otherwise it's a bit superfluous */
for (Z in { F, B }) /* F: front, B: back */
push((square[XY], frustum[XYZ]), stack)
}
} while (stack)
/* the given frusta (or parts of them) are in the buckets now: bucket_sort these buckets */
for (XYZ in front_to_back) /* front_to_back is a front-to-back enumeration of the the cube's octants; this depends on the eye's octant (predetermined lists) */
if (stack[XYZ]) /* if the stack associated with subcube "cube[XYZ]" isn't empty */
bucket_sort(cube[XYZ], stack[XYZ])
}
}
/* Frusta are the discriminants. Classical Bresenham traverses a raster/grid; this Bresenham traverses a hierarchy.
Here the other dimension of space is the major axis: scale. */
hierarchical_bresenham(cube, frustum, square)
{
if (cube is a point) { /* leaf node */
square.color = cube.color
square.black = true /* i.e., is opaque */
} else { /* non-leaf */
if (frustum in cube[XYZ]) /* descend in non-empty subcube "cube[XYZ]" */
hierarchical_bresenham(cube[XYZ], frustum, square)
else { /* octosect frustum, quadrisect square */
for (Z in { F, B }) /* front frusta first */
for (XY in { LU, RU, LD, RD })
if (!square[XY].black)
hierarchical_bresenham(cube, frustum[XYZ], square[XY])
if (square[XY].black for all XY in { LU, RU, LD, RD })
square.black = true /* the region is opaque */
}
}
}
/* Here we quadrisect the frustum across x & y (no bisection across z) */
bucket_sort(frustum, square, list)
{
if (unit square) { /* frustum corresponds to a pixel */
cube = pop_front(list) /* pick a front cube */
square.color = cube.color
} else {
do {
cube = pop_front(list)
if (cube in frustum[XY] for some XY in { LU, RU, LD, RD }) /* frustum[XY] is the XY quadrant of frustum */
push_back(cube, list[XY]) /* list[XY] is a list of cubes that are in the XY subfrustum */
else /* the current cube spans subfrusta: push_front its (non-empty) children on list, in back-to-front order so that the frontmost are firsts */
for (XYZ in back_to_front) /* back_to_front depends on where the eye is with respect to cube's halfway planes (predetermined list) */
push_front(cube[XYZ], list) /* cube[XYZ] is the XYZ (non-empty) octant of cube */
} while (list)
for (XY in { LU, RU, LD, RD }) /* LU: left-up quadrant, RU: right-up quadrant etc. */
if (list[XY]) /* if there are cubes in the XY subfrustum */
bucket_sort(frustum[XY], square[XY], list[XY])
}
}
One thing that made him "not at all happy" was, perhaps, the use of * to sort octree nodes' bounding boxes among subfrusta/buckets (with the help of e.g., BoxOnPlaneSide).When trying to build a search algorithm that scans 3D atoms your biggest problems are what do you grab when they are far away and small ? How do you know whether an object is blocked by another object ? In the end perfect solutions were found. I don’t use the word perfect lightly, for a long time I was not at all happy with some parts of the system but they kept evolving and now we are at the point were the core of our algorithm is less then two pages long and it has no multiplication or division (multiply and divide slow a computer down). (cf. this)
While I agree people are often too prone to criticism without an open mind statements calling it a "new de facto standard renderer" puts you on the side of belief before proof. Is cold fusion the defacto standard energy source? Some people still think it will be, but it's a long way from being proven let alone standard.UD is the new de facto standard renderer, it accords very well with Sutherland's survey: a (combinatorial) coherent bucket sort.
Instead of indulging in destructive criticism, we should try to reverse engineer it.
In UD's case there already are big game companies using it. UD's algorithm is too elegant, simple, not to conquer the concerned field. This is why we should reverse it, before it's disclosed (and there remains very little time). As it stands, it's unclear how one would implement it on the current GPUs. This is a treath for the GPUs, not for UD.Some people still think it will be, but it's a long way from being proven let alone standard.
In UD's case there already are big game companies using it. UD's algorithm is too elegant, simple, not to conquer the concerned field.
Errr... Nobody's using it, except maybe some indie devs. There are too many issues with UD for it to be practical for a big-name title from a large publisher. You can't animate stuff with it, everything looks the same everywhere or you run out of RAM immediately, etc.In UD's case there already are big game companies using it.
Certainly UD is nothing like a new de facto engine that all the big companies are switching to.
Not all but at least one "dependent" quite big game company uses it (as of April 23, 2012).Errr... Nobody's using it, except maybe some indie devs.
http://www.youtube.com/watch?v=_5hg9VfbyYgAFAIK there hasn't yet been a showcase of UD with dynamic lights, or shadows, or animated content.
They page in the needed nodes.You can't animate stuff with it, everything looks the same everywhere or you run out of RAM immediately, etc.
Not all but at least one "dependent" quite big game company uses it (as of April 23, 2012).
DICE.Who?