
|
Search
Search Loci: |
LociConstructing Mathlets Quickly using LiveGraphics3DOcclusions of ObjectsIn a three-dimensional image it often happens that one object is in front of another. LiveGraphics3D handles these occlusions by sorting graphics primitives according to their distance from the viewer (i.e. you). Objects which are furthest away are drawn first, and the nearest objects are drawn last; we say they "occlude" the objects behind them. In computer graphics this process is often referred to as the "painter's algorithm." It is simple to implement and works well in most cases, but it can have unintended consequences. To demonstrate how things can go awry, we'll construct a sphere surrounded by a ring. There is a right way and a wrong way to implement this in LiveGraphics3D. (* A function to convert from spherical to rectangular coordinates *) f[r_, t_, p_] = {r*Sin[p]Cos[t], r*Sin[p]Sin[t], r*Cos[p]}; (* Define dt and dp for use below *) n = 20; tmin = 0; tmax = 2Pi; dt = (tmax - tmin)/(2n); pmin = 0; pmax = Pi; dp = (pmax - pmin)/n; (* Create a light gray sphere of radius 1 *) sphere = { RGBColor[0.8, 0.8, 0.8], Table[ Polygon[{f[1, i, j], f[1, i + dt, j], f[1, i + dt, j + dp], f[1, i, j + dp]}], {i, tmin, tmax - dt/2, dt}, {j, pmin, pmax - dp/2, dp}]}; (* Create a ring of radius 1.1 around the sphere *) badRing = {Thickness[0.01], RGBColor[0, 0, 1], Line[ Table[f[1.1, i, Pi/2], {i, tmin, tmax, dt}] ]}; (* Another way to create the ring of radius 1.1 *) goodRing = {Thickness[0.01], RGBColor[0, 0, 1], Table[ Line[{f[1.1, i, Pi/2], f[1.1, i + dt, Pi/2]}], {i, tmin, tmax, dt}]}; Before continuing, make sure you understand the difference between the two rings defined above.
Line[{{1.1, 0, 0}, {1.08646, 0.172078, 0}, ..., {1.1,, 0, 0}}]
By contrast,
{Line[{{1.1, 0, 0}, {1.08646, 0.172078, 0}}], Line[{{1.08646, 0.172078, 0},
Within Mathematica, we could use either In LiveGraphics3D the two versions are definitely not equivalent, as demonstrated by following applets.
The painter's algorithm in LiveGraphics3D is applied primitive-by-primitive. The applet computes a single distance for each primitive, and the depth-sorting is based on these distances. However, most primitives are extended in space and, therefore, are not sufficiently characterized by a single distance. This causes the problem with our ring.
On the other hand, This example demonstrates an important rule of thumb: to avoid occlusion errors, it is almost always beneficial to break up large primitives into a collection of smaller objects. In particular, you should always replace You might ask whether one could solve this sorting problem by taking into account the interval of distances occupied by a primitive in some clever way. Unfortunately, there are cases, so-called "cyclic occlusions," which just cannot be sorted, regardless of the sorting criterion. Here is a simple example with four straight
(* A Graphics3D object consisting of four Lines
which shows occlusions errors for certain view points *)
Graphics3D[{Thickness[0.05],
RGBColor[1,0,0], Line[{{-2,-1,-1}, {2,-1,1}}],
RGBColor[0,1,0], Line[{{1,-2,-1}, {1,2,1}}],
RGBColor[0,0,1], Line[{{2,1,-1}, {-2,1,1}}],
RGBColor[1,0,1], Line[{{-1,2,-1}, {-1,-2,1}}]},
PlotRange -> {{-2, 2}, {-2, 2}, {-1, 1}},
ViewPoint -> {0,0,4}, ViewVertical -> {0,1,0},
Boxed -> False]
For the default view point, each colored line segment should occlude one other line and be occluded by another. This cyclic occlusion cannot be rendered correctly with four
The correct rendering in the second applet was achieved by splitting each
(* A Graphics3D object consisting of eight Lines
which shows cyclic occlusions for certain view points *)
Graphics3D[{Thickness[0.05],
RGBColor[1,0,0], Line[{{-2,-1,-1},{0,-1,0}}],Line[{{0,-1,0},{2,-1,1}}],
RGBColor[0,1,0], Line[{{1,-2,-1},{1,0,0}}],Line[{{1,0,0},{1,2,1}}],
RGBColor[0,0,1], Line[{{2,1,-1},{0,1,0}}],Line[{{0,1,0},{-2,1,1}}],
RGBColor[1,0,1], Line[{{-1,2,-1},{-1,0,0}}],Line[{{-1,0,0},{-1,-2,1}}]
},
PlotRange -> {{-2, 2}, {-2, 2}, {-1, 1}},
ViewPoint -> {0,0,4}, ViewVertical -> {0,1,0},
Boxed -> False]
A popular alternative to the "painter's algorithm" is the use of depth buffering (also called z-buffering), which avoids all of the occlusion problems discussed here. In order to support older web browsers and Java virtual machines, LiveGraphics3D is restricted to Java 1.1, which, unfortunately, does not support depth buffering. Therefore, LiveGraphics3D does not employ depth buffering and suffers from frequent occlusion errors. While these errors are inconvenient, you should remember that this approach enables many users to view mathlets based on LiveGraphics3D without downloading and installing a more recent Java virtual machine. The decision to restrict LiveGraphics3D to Java 1.1 is based on the popularity of web browsers that are limited to Java 1.1. At the time of writing this article, there is still at least one very popular Java virtual machine that is limited to Java 1.1. However, as soon as such Java virtual machines are no longer installed on a large amount of computers, the restriction of LiveGraphics3D to Java 1.1 certainly has to be reconsidered. For a further discussion of sorting 3D objects, see "Writing Mathlets III" (Leathrum, 2002). |