|
|
My isoblob folly has gone along reasonably well, though I've had a
little trouble with the surface normals. Some of the normals seem to be
rather off from their expected values, still.
Anyway, I got to thinking: Normal calculation in an isoblob is done in
much the same way an isosurface does it: It looks at nearby values of
the function, and decides which way to point the x, y, and z coordinates
of the normal based on that. Makes sense, but there's a better way.
The isosurface function module was created with two types of
calculation: An ordinary function value at a given set of x,y,z
coordinates, and an interval calculation--used to solve for the
function's zeroes--expressing the minimum and maximum possible values
given a certain input of variables.
Why not make it possible to calculate the function normal at a given
point?
All this will take is a little calculus, and I've already worked out the
formulas needed to do it. Most of it, that is--noise3d() will need some
more careful work to get an accurate normal.
The idea is quite simple: A normal is calculated by partial derivatives,
and every function or variable or constant can have its derivative
calculated. With the chain rule thrown in, things get really simple. For
example:
d(x+y)=dx+dy
d(x*y)=y*dx+x*dy
If x represents a <1,0,0> in the normal stack value, and y is a <0,1,0>,
then an addition operation can add the two normals together, and a
multiplication can use the formula above, applying the actual values of
x and y to multiply by the two different normals.
More concisely:
Operator stack: x y +
(Assume <3,4,5> is the point to evaluate.)
Operation Calc stack Normal stack
x 3 <1,0,0>
y 4 <0,1,0>
* 12 <4,3,0> // y*dx + x*dy
The beauty of this stack method is that this normal stack carries the
actual derivitave of (x*y), so any function that uses (x*y) as a value
can use not only the value itself (12), but also its derivitive for the
chain rule.
So, (x+y)*z would look like this:
Operation Calc stack Normal stack
x 3 <1,0,0>
y 4 <0,1,0>
+ 7 <1,1,0> // d(x+y) = dx+dy
z 5 <0,0,1>
* 35 <5,5,7> // z*d(x+y) + (x+y)*dz
To verify it, try x*z+y*z:
Operation Calc stack Normal stack
x 3 <1,0,0>
z 5 <0,0,1>
* 15 <5,0,3> // z*dx + x*dz
y 4 <0,1,0>
z 5 <0,0,1>
* 20 <0,5,4> // z*dy + y*dz
+ 35 <5,5,7> // add tops of stack
The main drawback is that standard vector multiplication won't do,
because r, s, t, u, and v are also possible variables (the isoblob uses
<r,s,t>). Including the standard <x,y,z>, that's eight partial
derivatives to keep track of. This means the calculation stack is 9x the
size, for all intents and purposes--but most of the calculation done
with the excess is mere multiplication and addition.
The main advantages are these: The function only needs to be evaluated
once, at the point of intersection, instead of four times (as in the
isosurface normal routine), *and* it's completely accurate. The only
exception, as I mentioned, was noise3d(), which doesn't have an
easy-to-find normal; better to fudge that one.
All this can be done fairly smoothly by modifying f_func.c and its
support files. A few quick #define statements can take care of the task
of handling 8 different derivatives at a time, too, treating the bunch
as a giant 8-dimensional vector. I may get started on this shortly,
since it could be the salvation of the isoblob.
Interesting concept, no?
Lummox JR
Post a reply to this message
|
|
|
|
The function-normal patch is alive and well. It had some weird bugs at
first, but all seems to be working now. The patch just includes support
for the normal calculation, though, and doesn't actually put it to any
use.
I've only applied the new normal calculation to the isoblob I've been
working on, as a test. Turns out, the isoblob wasn't working right at
all with the old isosurface-style method of finding a normal (I have no
clue why), but it works just fine with the new one. An example of the
difference is posted on povray.binaries.images -- just look for
"isoblob" and look for the two images I posted with cylinders and a
cube.
So far the normal calculation appears to be accurate and working just
great, including (it seems) for noise3d(). func3d(), for nested
functions, remains untested as yet, but I may try out something to see
if it works.
Two spots I had to fudge the normal in were noise3d(), the derivative of
which would be murder to calculate precisely even if I had a clue how,
and in evaluating a pre-defined function. The pre-defined isosurface
functions (sphere, helix, etc.) wouldn't be too hard to find normals
for, but I for one don't feel like altering those functions *and* their
basic syntax. Close approximation is good enough for them.
So, it seems like this was a good idea after all. Maybe sometime I'll
apply the normal calculation to isosurfaces and possibly parametric
surfaces, and see how that works out. I have a feeling it will be even
better than the results I've had with isoblobs, since the isoblob is
still in a very experimental (i.e. flaky) stage.
Lummox JR
Post a reply to this message
|
|