Original Post
Hi, I am currently writing my first raytracer (awwww). So far i've got diffuse lighting lighting, specular lighting, shadows + reflection going, but I don't quite get the refraction algorithm. as i understand it, light bends whenever it undergoes a change in material. So a ray bends as it enters an object (say, a sphere), and also when it exits it. Bending the ray when it goes in according to snell's law is easy enough, but how do you bend it when it leaves? Given that the ray/primitive intersection routines i've been using (for spheres, at least) will allow collisions with arbitrary points inside the sphere as well as on its surface, what is an easy, primitive-indepedant way of calculating the exit point from the primitive? I am using this method to *try* and apply both entering-object and exiting-object transitions. FYI the last argument to Refract indicates that it should reverse the materials (ie exiting material into open air rather than entering it). The problem is, when i cast the second ray into the object to try and find out where it exits, the returned distance is always ~0, presumably because the collision detection function counts any point of a ray being inside an object as a collision. Collision detection for sphere and finally, refraction calculation Any help would be great, i am well and truely stuck :)
bool Raytracer::completeRefract (Object * obj, Vector3d &point, Vector3d &dir, int currentDepth, Ray &final)
{
//Create reflection ray
Vector3d N1 = obj->getNormal (point);
Vector3d Ref1;
//If ray can be refracted without total internal reflection . . .
if (refract (obj, point, dir, Ref1, true))
{
//Calculate next intersection
Ray R1 = Ray (point + (Ref1 * (0.01)), Ref1);
float D1;
Object * O1 = Raytrace (R1, &D1);
//If we hit ourself again, refract out
if (O1 = obj)
{
Vector3d point2 = point + (Ref1 * D1);
Vector3d Ref2;
if (refract (obj, point2, Ref1, Ref2))
{
final = Ray (point2, Ref2);
return true;
}
}
//Otherwise, gone through an object without volume, return single-refracted ray
final = Ray (point, Ref1);
return true;
}
//Failure - total internal refraction
return false;
}
float Sphere::testIntersection(Ray &r, float maxDist)
{
Vector3d dir = r.getDirection();
Vector3d org = r.getOrigin();
Vector3d dst = r.getOrigin() - position;
//Set up and solve quadratic equation
float b = dst.dot (dir) * 2.0f;
float c = dst.dot (dst) - radius;
float disc = b * b - 4 * c;
//No real roots
if (disc < 0)
return -1;
float sqrtDisc = sqrt (disc);
float t0 = (-b + sqrtDisc) * 0.5f;
float t1 = (-b - sqrtDisc) * 0.5f;
if (t0 < t1 && t0 < maxDist)
return t0;
else if (t1 < maxDist)
return t1;
//if (t0 < maxDist)
// return t0;
return -1;
}
bool Raytracer::refract (Object * obj, Vector3d &point, Vector3d &dir, Vector3d &final, bool reverseMediums)
{
//Get objects refractive index
float refractionIndex = obj->getMaterial()->getRefractiveIndex();
//Ratio of object / air
float n = refractionIndex / rIndexAir;
if (reverseMediums)
n = 1.0f / n;
//Get normal at point we are refracting from
Vector3d N = obj->getNormal (point);
if (reverseMediums)
n *= 1.0f;
//cosine of angle of dir to normal
float cosI = -1.0f * N.dot ( dir);
float cosT2 = 1.0f - n * n * (1.0f - cosI - cosI);
//If cosT2 < 0, total internal reflection occured so no refraction
if (cosT2 < 0)
return false;
//Otherwise, calculate refraction ray
final = (dir * n) + (N * (n * cosI - sqrtf (cosT2)));
final.normalise();
return true;
}