cones

Henry Cejtin henry@sourcelight.com
Thu, 5 Oct 2000 02:52:58 -0500


Well,  I  fixed part of the cone problem.  First I noticed that my code was a
bit silly in computing the normal vector to the surface of the cone, but  not
incorrect.  The code to compute the normal should be

    fun normal () =
           let val Point.T = { x, y, z } = here ()
           in Vec.unitize (Vec.T { x = x, y = ~ y, z = z })
           end

instead of

    fun normal () =
           let val Point.T = { x, y, z } = here ()
           in Vec.unitize (Vec.T (if y >= 0.0
                                     then { x = x / y,
                                            y = ~ 1.0,
                                            z = z / y }
                                     else { x = ~ x / y,
                                            y = 1.0,
                                            z = ~ z / y }))
           end

Note,  they  really  do  compute the same thing, the second one is just being
stupid (except when y = 0, which is a singularity any way).

The actual  problem  I  fixed  was  caused  by,  of  course,  floating  point
arithmetic.

Actually,  there  are two problems I think.  The first problem has to do with
the connection between an incoming and outgoing intersection and the fact dot
product  between the direction you are going and the normal.  You should only
be leaving (going out) if the dot product is positive.  I'm  sure  that  this
was  not  correct  because  the floating point computation of x^2 + z^2 - y^2
having roundoff errors.  The next problem, which again only  happens  if  you
start very close to the surface of the cylinder, is that sometimes that first
intersection should be skipped because you are  on  the  other  side  of  it.
I.e.,  if  I  compute  the illumination of a point on the cone, I am going to
start from the surface of the cone, and that ray had better not intersect the
same cone on that same side (thinking it is coming out).

The grotesque hack I put to handle these two cases is

    local
       fun fixup (surface, ray) =
              let val res as T { startsInside, meets } =
                         coneInf (surface, ray)
              in case meets of
                   [] => res
                 | Meet.T { normal, scale, ... }::rest =>
                      let val Ray.T { dir, ... } = ray
                          val startsInside' = Vec.dot (normal (), dir) > 0.0

                      in if scale < 0.000001
                            then T { startsInside = not startsInside',
                                     meet = rest }
                            else T { startsInside = startsInside',
                                     meets = meets }
                      end
              end
    in val coneInf = fixup
    end

(Note  the  10^-6  epsilon.)  With this tweak, all of the `surface acne' went
away, but the reflections in the cone surfaces still  are  not  correct.   It
almost  looks  as  if  you  are  seeing reflections of the intersecting half-
spaces.  I didn't look at the intersection code at all, but my guess is  that
the  remaining  bug  is  something  there.   I'll think tonight for a cleaner
solution to this problem then the 10^-6 epsilon.

Note, with this fix in, if you get rid of all the  specular  reflections,  it
looks fine, but if you use only specular ones, it definitely seems wrong.