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
icq 429048706
