0

I'm following the steps in the book 'Ray Tracing in a weekend' and I'm having some problems with the section 11.3 Total internal reflection. When I try create the image I get this result:

Left sphere with refraction

This is the code for the intersection with the sphere, it returns an intersection with the sphere or null:

std::optional<Intersection> Sphere::intersect(const Ray& r, double tMin, double tMax) const {
    vec o_c = this->center- r.origin();

    auto a = r.direction().length_squared() () 
    auto b = dot(r.direction(), o_c);
    auto c = o_c.length_squared() - radius * radius;
    
    auto d = b * b - a * c;

    // at^2 + tb + c = 0 not real solution

    if (d < 0) {
        return std::nullopt;
    }

    // Verify root in [tMin, tMax]

   double dSqrt = std::sqrt(d);
    double root = (b - dSqrt) / a;
    if (root < tMin || root > tMax) {
        root = (b + dSqrt) / a;
        if (root < tMin || root > tMax) return std::nullopt;
    }

    Intersection i;
    i.t = root;
    i.point = r(i.t);
    i.normal = unit_vector(i.point - center);
        i.material = material;

    return i;
 };

This is the code for the refraction:

inline vec refraction(const vec& v, const vec& normal, double refraction_index) {
    vec rparal{ .0, .0, .0 };
    vec rperp{ .0, .0, .0 };

    bool front_face = (dot(v, normal) < 0);
    double ri = front_face ? 1.0 / refraction_index : refraction_index;

    vec outward_normal = front_face ? normal : -normal;
    
    double cos_tita = fmin(dot(-v, outward_normal ), 1.0);
    double sen_tita = sqrt(1 - cos_tita * cos_tita);

    
    if (ri * sen_tita > 1) {
        return reflect(v, outward_normal);
    }
    
    rperp = ri * (v + (dot(-v, outward_normal) * outward_normal)); 
    rparal = (-sqrt(abs(1 - rperp.length_squared()))) * outward_normal;

    return rparal + rperp;
};

And this is the code for the color_ray:

color color_ray(const Ray& r, int deep, const Geometry& g) {

    if (deep <= 0)
        return color(0, 0, 0);

    auto i = g.intersect(r, 0.001, infinity);

    if (i) {
        color intensity;

         // Check if the material is reflective
        if (i->material->reflective) {

            vec reflectedVector = reflect(unit_vector(r.direction()), i->normal) + 
            (i->material->coef_Reflective * vec::random_Unit_vector());

            Ray reflected_Ray = Ray(i->point, reflectedVector);
            intensity = i->material->DifuseColor * color_ray(reflected_Ray, deep - 1, g);

         // Check if the material is refractive
        } else if (i->material->refractive) {

           vec refractedVector = refraction(unit_vector(r.direction()), i->normal, 
                                 i->material>refraction_index);

       Ray refracted_Ray = Ray(i->point, refractedVector);
           intensity = color_ray(refracted_Ray , deep - 1, g);

        } else {

            intensity = i->material->DifuseColor * color_ray(Ray(i->point, i->normal +                    vec::randomUnitVector()), deep - 1, g);

        }

        return intensity;
    }
    
    vec unit_direction = unit_vector(r.direction());
    auto a = 0.5 * (unit_direction .y() + 1);
    return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
};

I should get the following image as a result:

Image from Ray Tracing in a weekend section 11.3

Let me know if you need more information, any help is appreciated :)

4
  • The quadratic formula doesn't look correct to me. The determinant is typically expressed as b*b - 4*a*c and a root as (-b +/- sqrt(det))/(2*a) - even if the coefficients have been scaled, both expressions can't be right in that code. Commented Jun 7, 2024 at 6:45
  • Hello @BrettHale, thanks for the answer. I followed the steps in the section 6.2 and in the section 6.3, where it simplifies the code for the intersection with the sphere, you can check that if you want. I checked the code again just to be sure and I think that there is not an error there, but I will go through it again just in case, thanks! Commented Jun 7, 2024 at 9:40
  • In the book b = -2 * dot (r.direction(), o_c), if we use h = dot (r.direction(), o_c) and b = -2h we will get the ecuations in the code for the root and the discriminant . Let me know if I am missing anything. Commented Jun 7, 2024 at 9:51
  • I just posted the solution, thanks for commenting @BrettHale, have a nice day! Commented Jun 7, 2024 at 10:11

1 Answer 1

0

Well, I found the solution, the problem was in

rparal = (-sqrt(abs(1 - rperp.length_squared()))) * outward_normal;

I deleted the parenthesis and I changed 'abs' for 'fabs' and that solved the problem.

rparal = -sqrt(fabs(1 - rperp.length_squared())) * outward_normal;

Here is the new image generated: New image without problem

Sign up to request clarification or add additional context in comments.

1 Comment

You could just use std::abs (also for the other maths functions) to select the right version depending on your arguments. Everything in your code should be either float or double. You might also want to increase your compiler's warning levels, as this looks like loss of precision your compiler should tell you about.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.