POV-Ray : Newsgroups : povray.programming : cones.cpp: A typo and a question. Server Time
28 Jul 2024 08:35:03 EDT (-0400)
  cones.cpp: A typo and a question. (Message 1 to 10 of 11)  
Goto Latest 10 Messages Next 1 Messages >>>
From: Massimo Valentini
Subject: cones.cpp: A typo and a question.
Date: 4 Oct 2002 08:24:39
Message: <3d9d8887@news.povray.org>
...

t1 = (-b + d) / a;
t2 = (-b - d) / a;

z = P[Z] + t1 * D[Z];

if ((t1 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= 0.0) && (z <=
1.0))
{
  Intersection[i].d   = t1 / len;
  Intersection[i++].t = SIDE_HIT;
}

z = P[Z] + t2 * D[Z];

if ((t2 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= 0.0) && (z <=
1.0))
{
  Intersection[i].d   = t2 / len;

...

This is a snippet from cones.cpp (lines ~ 220-235). In the second 'if'
I think the second test should be .. && (t2 < Max_Distance). And the
same typo is at line 291 in the same file.

The question concerns the divisions t1 / len  and  t2 /len. I think
that you should test for the validity of the intersection after the
division because otherwise you could discard some objects based on
a 'transformed' distance rather the real one.

The following example show the problem, you have 2 objects of the same
size and at the same distance from the camera. With a value of 100000
for the variable Scale you see only one, while if you use a value of
10000 you'll see both. The simple way to solve it, is to do the division
before the test. An other one could be to avoid the normalization of the
direction of the ray, it appears to me useless.

HTH Massimo


#declare Scale=100000;

camera { location <30*Scale, 0, 0>  look_at <0, 0, 0> }
light_source { <15*Scale, 0, 0>, rgb <1, 1, 1> }

cylinder {
  <0, 0, 0>, <-1, 0, 0> Scale
  pigment { color rgb <1, 1, 0> }
  scale <1, 5, 10>
  translate <0,0,10*Scale>
}
cylinder {
  <0, 0, 0>, <-1, 0, 0> 10*Scale
  pigment { color rgb <1, 0, 0> }
  scale <.1, .5, 1>
  translate <0,0,-10*Scale>
}


Post a reply to this message

From: Le Forgeron
Subject: Re: cones.cpp: A typo and a question.
Date: 7 Oct 2002 09:00:04
Message: <3DA1855B.1030203@free.fr>
Massimo Valentini wrote:


> 
> This is a snippet from cones.cpp (lines ~ 220-235). In the second 'if'
> I think the second test should be .. && (t2 < Max_Distance). And the
> same typo is at line 291 in the same file.
> 


I seems to agree with you, at least on the t1 vs t2 issue.


> The question concerns the divisions t1 / len  and  t2 /len. I think
> that you should test for the validity of the intersection after the
> division because otherwise you could discard some objects based on
> a 'transformed' distance rather the real one.
> 


I do not see why there is a limitation at Max_Distance,
I would simply remove that part of the test (It looks like an old 
optimisation: if it is enough away, remove it... it's bad, IMHO).


[...]

> An other one could be to avoid the normalization of the
> direction of the ray, it appears to me useless.

It is NOT useless. When CSG operation/object (union & merge, at least) 
will ask its component, it will orders the intersections by depth order 
before answering to the upper level. Thus, putting back the depth from 
object space to global space is mandatory (otherwise, strange things 
will happens).

Same goes when more than a single cone/cylinder get intersected by the 
ray, the engine need to order them correctly, in the global space!


Post a reply to this message

From: Massimo Valentini
Subject: Re: cones.cpp: A typo and a question.
Date: 7 Oct 2002 12:34:15
Message: <3da1b787@news.povray.org>
"Le Forgeron" ha scritto
:
: > An other one could be to avoid the normalization of the
: > direction of the ray, it appears to me useless.
:
: It is NOT useless. When CSG operation/object (union & merge, at least)
: will ask its component, it will orders the intersections by depth order
: before answering to the upper level. Thus, putting back the depth from
: object space to global space is mandatory (otherwise, strange things
: will happens).
:
: Same goes when more than a single cone/cylinder get intersected by the
: ray, the engine need to order them correctly, in the global space!
:

I try to show what I mean. You are first scaling the direction

<CODE src=cones.cpp lines=197-200>
MInvTransDirection(D, Ray->Direction, Cone->Trans);

VLength(len, D);
VInverseScaleEq(D, len);
</CODE>

This last line is a macro for a vector/scalar division:
D = D / len

Doing so results in a scaling of the coefficients (a & b)
of the 2nd order equation you are going to solve:

<CODE src=cones.cpp lines=206-213>
a = D[X] * D[X] + D[Y] * D[Y];

if (a > EPSILON)
{
  b = P[X] * D[X] + P[Y] * D[Y];

  c = P[X] * P[X] + P[Y] * P[Y] - 1.0;
</CODE>


And now you are required to scale the solution:

<CODE src= cones.cpp lines=220-228>
t1 = (-b + d) / a;
t2 = (-b - d) / a;

z = P[Z] + t1 * D[Z];

if ((t1 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= 0.0) && (z <=
1.0))
{
  Intersection[i].d   = t1 / len;
</CODE>


Suppose not to scale D, I name this not scaled direction D`
and D the direction that's actually used. It is:

D` = D * len

The resulting equation would be

a` * t`^2 + b` * t` + c = 0

where

a` = D`[X] * D`[X] + D`[Y] * D`[Y]
   = (D[X] * D[X] + D[Y] * D[Y]) * len ^ 2
   = a * len ^ 2

and b` = b * len

and whose solutions are t1,2` = t1,2 / len.

a` * t`^2 + b` * t` + c =
a * len^2 * (t / len)^2 + b * len * (t / len) + c =
a * t ^ 2 + b * t + c = 0

Exactly the previous solution only already scaled, so ready to
be used in global space sorting, testing, whatever necessary.

And

z` = P[Z] + t` * D`[Z]
   = P[Z] + (t / len) * (D[Z] * len)
   = P[Z] + t * D[Z] = z

The same z ready to be tested against 0 and 1.

So avoiding the normalization of the direction only saves a sqrt
(no need to calculate len) and some multiplications and divisions
but gives you the same result this function is returning.

The same reasoning is valid for the cone intersections.

If you want to try what I'm suggesting, assign 1.0 to len where this
is declared, and then comment out the computation of len.

<CODE lines=188-200>
  DBL a, b, c, z, t1, t2, len = 1.0;//<------
  DBL d;
  VECTOR P, D;

  Increase_Counter(stats[Ray_Cone_Tests]);

  /* Transform the ray into the cones space */

  MInvTransPoint(P, Ray->Initial, Cone->Trans);
  MInvTransDirection(D, Ray->Direction, Cone->Trans);

//  VLength(len, D);// <------------------------
//  VInverseScaleEq(D, len); //<----------------


HTH Massimo


Post a reply to this message

From: Le Forgeron
Subject: Re: cones.cpp: A typo and a question.
Date: 8 Oct 2002 05:30:40
Message: <3DA2A5CA.50109@free.fr>
Massimo Valentini wrote:

> "Le Forgeron" ha scritto
> :
> : > An other one could be to avoid the normalization of the
> : > direction of the ray, it appears to me useless.
> :
> : It is NOT useless. When CSG operation/object (union & merge, at least)
> : will ask its component, it will orders the intersections by depth order
> : before answering to the upper level. Thus, putting back the depth from
> : object space to global space is mandatory (otherwise, strange things
> : will happens).
> :
> : Same goes when more than a single cone/cylinder get intersected by the
> : ray, the engine need to order them correctly, in the global space!
> :
> 
> I try to show what I mean. You are first scaling the direction
> 


Ok... I understand better what you wanted.
I first thought that you wanted only to remove the division of the depth.

So, far, your suggestion looks ok, and it might deserves a study of the 
code, as well as experiments (just to check there is no adverse effect 
due to a precision problem...probably not, but I'm afraid of sturm 
solver effects). The study should also find out if there is any speed-up 
(or slow down) with the new code. (and by how much).


Post a reply to this message

From: Massimo Valentini
Subject: Re: cones.cpp: A typo and a question.
Date: 8 Oct 2002 06:45:19
Message: <3da2b73f$1@news.povray.org>
"Massimo Valentini" ha scritto 
: I try to show what I mean. You are first scaling the direction
: 
: <CODE src=cones.cpp lines=197-200>
: MInvTransDirection(D, Ray->Direction, Cone->Trans);
: 
: VLength(len, D);
: VInverseScaleEq(D, len);
: </CODE>
: 
: This last line is a macro for a vector/scalar division:
: D = D / len
: 
: Doing so results in a scaling of the coefficients (a & b)
: of the 2nd order equation you are going to solve:
: 
: <CODE src=cones.cpp lines=206-213>
: a = D[X] * D[X] + D[Y] * D[Y];
: 
: if (a > EPSILON)
: {

This solution is not equivalent to the original code.

This condition is different whether you scale the direction 
or not. May be there could be a way to avoid the square root (e.g.
expressing this condition if (a > EPSILON*len^2)) but it'd require
some work analyzing the code for both cones and cylinders. 

So you may either remove the condition for intersctions over 
Max_Distance, or shift the divisions t1,2/len before the test against 
Max_Distance. 

Massimo


Post a reply to this message

From: Le Forgeron
Subject: Re: cones.cpp: A typo and a question.
Date: 8 Oct 2002 10:03:48
Message: <3DA2E5CE.4090906@free.fr>
Massimo Valentini wrote:

> "Massimo Valentini" ha scritto 

> This solution is not equivalent to the original code.
> 
> This condition is different whether you scale the direction 
> or not. May be there could be a way to avoid the square root (e.g.
> expressing this condition if (a > EPSILON*len^2)) but it'd require
> some work analyzing the code for both cones and cylinders. 
> 


The (a > EPSILON) is used to avoid numerical error (division by 0 and 
alike, division by very small value gives a great inaccuracy)


EPSILON testing is also used later to avoid reflecting on the same 
object forever
(First ray hit the object, then you compute a ray from the hit point...
if EPSILON is not used, due to numerical error, the new ray may find 
again the same point [well, mathemacally, it is the same, but 
numerically it may be a little different so that testing would not 
detect it... hence the EPSILON : when the eye is near enough the 
surface, the surface should be removed... Epsilon is an arbitrary value,
so using EPSILON or EPSILON*len^2 should not be of too much concern for 
reasonnable value of len {ok, I might be contradicted by a scene...})

Sidenote: (a > EPSILON) and (t1 > EPSILON) show no correlation on the 
value of EPSILON, so there is probably no problem at all!


> So you may either remove the condition for intersctions over 
> Max_Distance, or shift the divisions t1,2/len before the test against 
> Max_Distance. 
> 


Test against Max_distance is unrelated with Epsilon value, and is any 
way a candidate for removal...


Post a reply to this message

From: Massimo Valentini
Subject: Re: cones.cpp: A typo and a question.
Date: 8 Oct 2002 13:00:15
Message: <3da30f1f@news.povray.org>
"Le Forgeron" ha scritto 
: Massimo Valentini wrote:
: 
: > This condition is different whether you scale the direction 
: > or not. May be there could be a way to avoid the square root (e.g.
: > expressing this condition if (a > EPSILON*len^2)) but it'd require
: > some work analyzing the code for both cones and cylinders. 
: > 
: 
: 
: The (a > EPSILON) is used to avoid numerical error (division by 0 and 
: alike, division by very small value gives a great inaccuracy)
: 

This is what I thought at first. But this condition has a geometrical 
meaning too. In fact 'a' is the square of the sin of the angle formed 
by the ray and the cylindrical axis, when calculated with ||D|| (length
of D, the norm) == 1, while when D isn't normalized it is the sin of the 
same angle multiplied by the square of len . 

Obviously, if the ray is (close to be) parallel to the axis, the solution 
is either impossible or indeterminate, the ray does not intersect the 
cylindrical surface or it lies entirely on it.

Given the fact that I'm not aware of the range of variability of len 
after transformation, it appears now a little more complex to evaluate
whether the removal of the normalization has an impact only on some 
border case (tiny cylinders in kilometers wide panoramas) or it's 
influencing some real and well looking scene.

I was thinking to suggest a simple fix to a border case strangeness 
with no impact on the great majority of the scenes, but now it appears 
that a little more testing is necessary for evaluating both the 
performance and the rendering impact. 

Massimo


Post a reply to this message

From: Massimo Valentini
Subject: Re: cones.cpp: A typo and a question.
Date: 12 Oct 2002 05:03:03
Message: <3da7e547@news.povray.org>
"Massimo Valentini" ha scritto
: : 
: : <CODE src=cones.cpp lines=206-213>
: : a = D[X] * D[X] + D[Y] * D[Y];
: : 
: : if (a > EPSILON)
: : {
: 
: This solution is not equivalent to the original code.
: 
: This condition is different whether you scale the direction 
: or not. May be there could be a way to avoid the square root (e.g.
: expressing this condition if (a > EPSILON*len^2)) but it'd require
: some work analyzing the code for both cones and cylinders. 
: 

It seems to be worth the effort, I patched the function intersect_cone 
in order to avoid the normalization of the ray direction and with some 
simple tests, based on scenes constituted mostly by cylinders, this 
patch saves a tenth of the total time and the image is the same produced 
by the original povray. 

Comments, hints and counter-examples are appreciated.

Massimo


Here's the patch and an example I used to time the differences:

static int intersect_cone(RAY *Ray, CONE *Cone, CONE_INT *Intersection)
{
  int i = 0;
  DBL a, b, c, z, t1, t2, squared_len;
  DBL d;
  VECTOR P, D;

  Increase_Counter(stats[Ray_Cone_Tests]);

  /* Transform the ray into the cones space */

  MInvTransPoint(P, Ray->Initial, Cone->Trans);
  MInvTransDirection(D, Ray->Direction, Cone->Trans);

  a = D[X] * D[X] + D[Y] * D[Y];
  squared_len = a + D[Z] * D[Z];

  if (Test_Flag(Cone, CYLINDER_FLAG))
  {
    /* Solve intersections with a cylinder */

    if (a > EPSILON * squared_len)
    {
      b = P[X] * D[X] + P[Y] * D[Y];

      c = P[X] * P[X] + P[Y] * P[Y] - 1.0;

      d = b * b - a * c;

      if (d >= 0.0)
      {
        d = sqrt(d);

        t1 = (-b + d) / a;
        t2 = (-b - d) / a;

        z = P[Z] + t1 * D[Z];

        if ((t1 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= 0.0) && (z <= 1.0))
        {
          Intersection[i].d   = t1;
          Intersection[i++].t = SIDE_HIT;
        }

        z = P[Z] + t2 * D[Z];

        if ((t2 > Cone_Tolerance) && (t2 < Max_Distance) && (z >= 0.0) && (z <= 1.0))
        {
          Intersection[i].d   = t2;
          Intersection[i++].t = SIDE_HIT;
        }
      }
    }
  }
  else
  {
    /* Solve intersections with a cone */

    a -= D[Z] * D[Z];

    b = D[X] * P[X] + D[Y] * P[Y] - D[Z] * P[Z];

    c = P[X] * P[X] + P[Y] * P[Y] - P[Z] * P[Z];

    if (fabs(a) < EPSILON * squared_len)
    {
      if (fabs(b) > EPSILON * sqrt(squared_len))
      {
        /* One intersection */

        t1 = -c / (2 * b);

        z = P[Z] + t1 * D[Z];

        if ((t1 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= Cone->dist) && (z <=
1.0))
        {
          Intersection[i].d   = t1;
          Intersection[i++].t = SIDE_HIT;
        }
      }
    }
    else
    {
      /* Check hits against the side of the cone */

      d = b * b - a * c;

      if (d >= 0.0)
      {
        d = sqrt(d);

        t1 = (-b - d) / a;
        t2 = (-b + d) / a;

        z = P[Z] + t1 * D[Z];

        if ((t1 > Cone_Tolerance) && (t1 < Max_Distance) && (z >= Cone->dist) && (z <=
1.0))
        {
          Intersection[i].d   = t1;
          Intersection[i++].t = SIDE_HIT;
        }

        z = P[Z] + t2 * D[Z];

        if ((t2 > Cone_Tolerance) && (t2 < Max_Distance) && (z >= Cone->dist) && (z <=
1.0))
        {
          Intersection[i].d   = t2;
          Intersection[i++].t = SIDE_HIT;
        }
      }
    }
  }

  if (Test_Flag(Cone, CLOSED_FLAG) && (fabs(D[Z]) > EPSILON))
  {
    d = (1.0 - P[Z]) / D[Z];

    a = (P[X] + d * D[X]);

    b = (P[Y] + d * D[Y]);

    if (((Sqr(a) + Sqr(b)) <= 1.0) && (d > Cone_Tolerance) && (d < Max_Distance))
    {
      Intersection[i].d   = d;
      Intersection[i++].t = CAP_HIT;
    }

    d = (Cone->dist - P[Z]) / D[Z];

    a = (P[X] + d * D[X]);

    b = (P[Y] + d * D[Y]);

    if ((Sqr(a) + Sqr(b)) <= (Test_Flag(Cone, CYLINDER_FLAG) ? 1.0 : Sqr(Cone->dist))
      && (d > Cone_Tolerance) && (d < Max_Distance))
    {
      Intersection[i].d   = d;
      Intersection[i++].t = BASE_HIT;
    }
  }

  if (i)
  {
    Increase_Counter(stats[Ray_Cone_Tests_Succeeded]);
  }

  return (i);
}


/*************************************/
#macro Dr(F,D) #local N = vlength(D);#declare D = D / N;
#while (N >= 0) cylinder {z,-z,0.3 translate F+D*N}
#declare N=N-1;#end#end

difference {
  cylinder { z*.09, z*.01, 9 }
  Dr(<-6, -3, 0>, 6*y)
  Dr(<-6, 3, 0>, <1.6, -2, 0>)
  Dr(<-3, 3, 0>,<-1.6, -2, 0>)
  Dr(<-3, -3, 0>, 6*y)
  Dr(<-2, -3, 0>, 5*y)
  Dr(<-1.75, 3, 0>, 3*x)
  Dr(<2, -3, 0>, 5*y)
  Dr(<-2, 0, 0>, 4*x)
  Dr(<3, -3.2, 0>, 1.1*<3, 6, 0>)
  Dr(<6, -3.2, 0>, 1.1*<-3, 6, 0>)
  pigment { color rgb <1, 0, 0> }
}
cylinder { <0,-100, 110> <0,100,110> 100 pigment {rgb <0,0,1>} }
light_source { <0, 0, -10>, rgb <1, 1, 1> }
camera {location <12, -15, -1.75> look_at 10*z }


Post a reply to this message

From: Le Forgeron
Subject: Re: cones.cpp: A typo and a question.
Date: 14 Oct 2002 09:03:04
Message: <3DAAC08C.3000600@free.fr>
Massimo Valentini wrote:

> "Massimo Valentini" ha scritto
> : : 
> : : <CODE src=cones.cpp lines=206-213>
> : : a = D[X] * D[X] + D[Y] * D[Y];
> : : 
> : : if (a > EPSILON)
> : : {
> : 
> : This solution is not equivalent to the original code.
> : 
> : This condition is different whether you scale the direction 
> : or not. May be there could be a way to avoid the square root (e.g.
> : expressing this condition if (a > EPSILON*len^2)) but it'd require
> : some work analyzing the code for both cones and cylinders. 
> : 
> 
> It seems to be worth the effort, I patched the function intersect_cone 
> in order to avoid the normalization of the ray direction and with some 
> simple tests, based on scenes constituted mostly by cylinders, this 
> patch saves a tenth of the total time and the image is the same produced 
> by the original povray. 
> 
> Comments, hints and counter-examples are appreciated.


NO MORE INLINE here please (even binary... ).

I made also some tests based on 3.1 sources (I'm not yet ready for 3.5) 
and get also a feeling of speed, but I forgot that I updated my compiler 
in the meantime, so the result are meaningless (until I revert to the 
old code and compile again...)

Mathematically, the correction is ok.
correcting the EPSILON factor is IMHO useless, just keep it "(a>EPSILON)".


Post a reply to this message

From: Christopher James Huff
Subject: Re: cones.cpp: A typo and a question.
Date: 14 Oct 2002 14:12:31
Message: <chrishuff-16DD67.14073214102002@netplex.aussie.org>
In article <3DA### [at] freefr>, Le Forgeron <jgr### [at] freefr> 
wrote:

> NO MORE INLINE here please (even binary... ).

Huh? It is attachments that are forbidden outside the binary groups, 
in-line code like this is fine. This was a bit long, but not 
unacceptable in my opinion.

-- 
Christopher James Huff <cja### [at] earthlinknet>
http://home.earthlink.net/~cjameshuff/
POV-Ray TAG: chr### [at] tagpovrayorg
http://tag.povray.org/


Post a reply to this message

Goto Latest 10 Messages Next 1 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.