transparencyraytracingtranslucency

Ray tracing, translucent sphere has a dot in centre


I'm building a ray tracer as an assignment. I'm trying to get refraction working for spheres and I got it half-working. The problem is I can't get rid of the black dot in the centre of the sphere

enter image description here

This is the code for the intersection:

double a = rayDirection.DotProduct(rayDirection);
double b = rayOrigin.VectAdd(sphereCenter.Negative()).VectMult(2).DotProduct(rayDirection);
double c = rayOrigin.VectAdd(sphereCenter.Negative()).DotProduct(rayOrigin.VectAdd(sphereCenter.Negative())) - (radius * radius);
double discriminant = b * b - 4 * a * c;

if (discriminant >= 0)
{
    // the ray intersects the sphere
    // the first root
    double root1 = ((-1 * b - sqrt(discriminant)) / 2.0 * a) - 0.000001;
    double root2 = ((-1 * b + sqrt(discriminant)) / 2.0 * a) - 0.000001;

    if (root1 > 0.00001)
    {
        // the first root is the smallest positive root
        return root1;
    }
    else
    {
        // the second root is the smallest positive root    
        return root2;
    }
}
else
{
    // the ray missed the sphere
    return -1;
}

This is the code responsible for computing the direction of the new refracted ray:

double n1 = refractionRay.GetRefractiveIndex();
double n2 = sceneObjects.at(indexOfWinningObject)->GetMaterial().GetRefractiveIndex();

if (n1 == n2)
{
    // ray inside the same material, means that it is going to be refracted outside, 
    n2 = 1.000293;
}

double n  = n1 / n2;

Vect I = refractionRay.GetRayDirection();
Vect N = sceneObjects.at(indexOfWinningObject)->GetNormalAt(intersectionPosition);

double cosTheta1 = -N.DotProduct(I);
// we need the normal pointing towards the side the ray is coming from
if (cosTheta1 < 0) 
{
    N = N.Negative();
    cosTheta1 = -N.DotProduct(I);
}

double cosTheta2 = sqrt(1 - (n * n) * (1 - (cosTheta1 * cosTheta1)));
Vect refractionDirection = I.VectMult(n).VectAdd(N.VectMult(n * cosTheta1 - cosTheta2));


Ray newRefractionRay(intersectionPosition.VectAdd(refractionDirection.VectMult(0.001)), refractionDirection, n2, refractionRay.GetRemainingIntersections());

When creating the new refracting ray, I tried adding the direction times a small value to the intersection position to make the origin of this new ray inside the sphere. The size of the black dot changes if I change that small value. If I make it too big the margins of the sphere start turning black as well.

If I add colour to the object it looks like this:

enter image description here

And if make that small constant bigger (0.1) this happens:

enter image description here

Is there a special condition I should take into account? Thank you!


Solution

  • You should remove the epsilon factors that you subtract when you calculate the two roots:

    double root1 = ((-1 * b - sqrt(discriminant)) / 2.0 * a);
    double root2 = ((-1 * b + sqrt(discriminant)) / 2.0 * a);
    

    In my experience the only place you need a comparison against epsilon is when checking whether the found root is along the path of the ray and not at its origin, per your:

    if (root1 > 0.00001)
    

    NB: you could eke out a little more performance by only doing the square root calculation once, and also by only calculating root2 if root1 <= epsilon