Raytracing speedup tricks


First hit optimization

Let's look at sphere and plane intersection check functions.

int Sphere :: Intersect ( Ray &ray, double &t )
{
 Vector l = Loc - ray.Org; // direction vector
 double L2OC = l.dotProduct(l); // squared distance
 double tca = l.dotProduct(ray.Dir); // closest dist to center
 double t2hc = sphR*sphR - L2OC + tca*tca;
 double t2;

 if ( t2hc <= 0.0 )
   return 0;

 t2hc = sqrt ( t2hc );

 if ( tca < t2hc ) // we are inside
  {
   t = tca + t2hc;
   t2 = tca - t2hc;
  }
  else // we are outside
   {
    t = tca - t2hc;
    t2 = tca + t2hc;
   }
 if ( fabs ( t ) < GeomThreshold )
  t = t2;
 return t > GeomThreshold;
}

int Plane :: Intersect ( Ray &ray, double &t )
{
 double vd = n.dotProduct(ray.Dir);

 if ( vd > -EPS && vd < EPS )
   return 0;

 t =(-(n.dotProduct(ray.Org)+D)) / vd;
 return t > GeomThreshold;
}

For first hit ray.Org=EyePos ALWAYS. In my implementation of raytracer the global variable Level is a depth of raytracing, for first hit Level=1.
We can precalculate L2OC = l.dotProduct(l) in Sphere::Intersect() and n.dotProduct(ray.Org) in Plane::Intersect() and store as a member of a Sphere and Plane class. Also we can store sphR*sphR in Sphere::Intersect():
class Sphere : public GObject
{
 public:
   Vector Loc; // center
   double Radius,L2OCeye;
   double Radius2; // squared radius

   Sphere (Vector c,double r )
    { Loc = c; Radius = r; Radius2 = r*r;min=c-r;max=c+r;
    c-=Eye;L2OCeye=c.dotProduct(c); };

   virtual int Intersect (Ray&, double&);
};

class Plane : public GObject
{    Vector n; // unit plane normal
   double D,NeyeD; // distance from origin

   Plane (Vector normal,double dist )
    { n = normal; D = dist;
    NeyeD=-((n.dotProduct(Eye))+D); };

   virtual int Intersect (Ray&, double&);
};

Rewrited our intersect functions:
int Sphere :: Intersect ( Ray &ray, double &t )
{
 Vector l = Loc - ray.Org;
 double L2OC = (Level==1)?L2OCeye:(l.dotProduct(l)); // *** changed
 double tca = l.dotProduct(ray.Dir);
 double t2hc = Radius2 - L2OC + tca*tca;// *** changed
 double t2;

 if ( t2hc <= 0.0 )
   return 0;

 t2hc = sqrt ( t2hc );

 if ( tca < t2hc )
  {
   t = tca + t2hc;
   t2 = tca - t2hc;
  }
  else
   {
    t = tca - t2hc;
    t2 = tca + t2hc;
   }
 if ( fabs ( t ) < GeomThreshold )
  t = t2;
 return t > GeomThreshold;
}
----------
Result: -4*

int Plane :: Intersect ( Ray &ray, double &t )
{
 double vd = n.dotProduct(ray.Dir);

 if ( vd > -EPS && vd < EPS )
   return 0;

 t =((Level==1)?NeyeD:(-((n.dotProduct(ray.Org) )+D))) / vd;// *** changed
 return t > GeomThreshold;
}
----------
Result: -4*


Shadow tests optimization

Look at the code snippet:
...
for( i = 0; i < NUM_LIGHTS; i++ )
{
 cLightVect = LightSources[i].Position - HitPoint;
 fLen = cLightVect.Normalize();
 fAngle = cLightVect.Dotproduct(cNormal);//normal to current object
 if( fAngle > 0.0f )
  {
   fShadow =(Intersect(HitPoint,cLightVect,fLen)!=0);
   if( fShadow > 0.0f )
     {
...

In this snippet of code we check did ray from hitpoint to light occluded by object.
We can find intersection not from hitpoint TO light, but FROM light to hitpoint.
If store same like with Eye operations results, it can give us some speedup.
Rewrited snippet:
...
for( i = 0; i < NUM_LIGHTS; i++ )
{
 cLightVect = LightSources[i].Position - HitPoint;
 fLen = cLightVect.Normalize();
 fAngle = cLightVect.Dotproduct(cNormal);//normal to current object
 if( fAngle > 0.0f )
  {
   LightNum=i+1; //global variable. if not from light, LightNum=0
   fShadow =(Intersect(LightSources[i].Position,-cLightVect,fLen)!=0);// *** changed
   if( fShadow > 0.0f )
     {
...
----------
Result: -3* per sphere or plane


---------------------------------------
Be optimized!
tigra
mail me
icq 429048706

Hosted by uCoz