II. Rendering

III. An Optimization

** Update ** (March 2011 - long overdue - my apologies) Ken Perlin came up with another nice basis function that works well for this purpose. The equation (graphed below) is: g(r) = 6r^5 - 15r^4 + 10r^3 ...or... g(r) = r * r * r * (r * (r * 6 - 15) + 10) One nice thing about this is that the range of r is from [0..1], rather than [0..0.707]. The other nice thing is that it's actually symmetrical about the midpoint, with zero-derivatives at both g(0) and g(1) - so you can use it for a kind of general "smoothing" function, for all kinds of purposes, even outside of graphics. Note that this curve is inverted from the curve we use for blobs; here, g(0) = 0, and g(1) = 1. With blobs, we want g(0) ~= 1, and g(1) ~= 0. Because of this equation's symmetry, you can invert either the domain or the range, and it will work (i.e. take g(1-r), or take 1-g(r)). The blue line is simply g(r) = r, while the red line is the 'smoothed' version, g(r) = 6r^5 - 15r^4 + 10r^3. For the latter, note the nice zero-slope at both r==0 and r==1, and the symmetry about the center. (now back to our main lesson...)

IV. Control

V. Miscellaneous

VI. BONUS UPDATES (5/24/01)

SPHERES ----------------------- isometric surface equation: x^2 + y^2 + z^2 = mag^2 radius = mag radius (mag) represents largest possible area of contribution -> the spherical bounding test uses the radius (squared). CUBES ----------------------- isometric surface equation: x^4 + y^4 + z^4 = mag^4 max radius = sqrt(sqrt(2.0))*mag [corners] min raduis = mag [flat edges] mag (min radius) represents 1/2 the length of a cube edge isometric volume represents largest possible area of contribution -> the spherical bounding test uses the max radius (squared). -> the bounding box test uses the min radius as the distance of the planes from the "origin." TORII ----------------------- basically, there's an "axis ring" whose radius is 'mag'. the largest possible area of contribution is a torus around that axis, of sub-radius 'inner_rad'. isometric surface equation: [closest dist. to axis ring]^2 = inner_rad^2 -> 'inner' radius can be greater than 'mag' (makes apples w/dimples), but it must not be more than 2x as big as it (untested circumstance). -> math note: the "closest distance" is a 3D function broken down into fake 2D: it gets the distance to it in y, and in x/z, then does a 2D distance on those. Slick! -> the spherical bounding test uses the radius 'mag + inner_rad'. -> this is followed by a clip at y=inner_rad and y=-inner_rad. -> this is follwed by a complicated test: [rc (radius of cylinder) = mag - inner_rad] if the entry point of the ray was cut off on a y-plane and that cut-off entry point is inside the cylinder x^2 + z^2 = rc^2, then start the span at the far end of the cylinder.

...and here's some (unoptimized for legibility) sample code:

if (blob[k].type == C_SPHERE) { dx = (p.x - blob[k].x); dy = (p.y - blob[k].y); dz = (p.z - blob[k].z); r_squared = (dx*dx + dy*dy + dz*dz); //enable this line if your blobs are of varying sizes: //r_squared /= (blob[k].mag*blob[k].mag); // since f(r) is valid when r is in the range [0-.707], // r_squared should be in the range [0-.500]. if (r_squared < 0.5f) // same as: if (sqrtf(r_squared) < 0.707f) { q += (0.25 - r_squared + r_squared*r_squared); } } else if (blob[k].type == C_CUBE) { dx = p.x - blob[k].x; dy = p.y - blob[k].y; dz = p.z - blob[k].z; dx *= dx; dy *= dy; dz *= dz; dx *= dx; dy *= dy; dz *= dz; r_4th = dx + dy + dz; if (r_4th <= blob[k].mag_4th) // r < 0.7*blob[k].mag ð r^2 < 0.5*blob[k].mag { r_4th *= blob[k].half_mag_inv_4th; q += (0.25 - r_4th + r_4th*r_4th); } } else if (blob[k].type == C_TORUS) { float dist_in_xz, dist_in_y, dist_from_track_squared; dx = p.x - blob[k].x; dy = p.y - blob[k].y; dz = p.z - blob[k].z; dist_in_xz = sqrt(dx*dx + dz*dz) - blob[k].mag; dist_in_y = dy; dist_from_track_squared = (dist_in_xz*dist_in_xz + dist_in_y*dist_in_y); if (dist_from_track_squared < blob[k].inner_rad*blob[k].inner_rad) { dist_from_track_squared *= 0.5/(blob[k].inner_rad*blob[k].inner_rad); q += (0.25 - dist_from_track_squared + dist_from_track_squared*dist_from_track_squared); } }