0
votes

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!

1
Note that you also have one on the reflection in the green sphere too. If it is relevant, I believe that the foreground sphere is colorless and clear rather than reflective? - user289086
I'd suggest checking your refraction equations first - the sphere intersection looks ostensibly valid (and that's supported by the correct rendering of the opaque spheres in the scene). - Alnitak
also, there shouldn't be any need to subtract that epsilon factor from the two roots so long as you ensure that the value of the roots found does exceed epsilon. These factors are only needed to ensure that newly generated rays starting on the surface of the sphere don't self intersect the sphere again. - Alnitak
oh, and your shadows look odd. They shouldn't be smaller than the balls that cast them. - Alnitak

1 Answers

1
votes

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