# T4 Refraction (Milestone 2)

### T4.1 Add refract() to calculate the refrac vector direction (vec3.h)

Add refract() in vec3.h to calculate the refrac vector direction

Add refract() at the end of vec3.h, outside the vec3 class.

```cpp
inline vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat) {
    auto cos_theta = std::fmin(dot(-uv, n), 1.0);
    vec3 r_out_perp =  etai_over_etat * (uv + cos_theta*n);
    vec3 r_out_parallel = -std::sqrt(std::fabs(1.0 - r_out_perp.length_squared())) * n;
    return r_out_perp + r_out_parallel;
}
```

### T4.2 Add dielectric material (material.h)

Add class dielectric in material.h

dielectric inherits from material and implements its scatter method using refract()

<pre class="language-cpp"><code class="lang-cpp"><strong>class material {
</strong>//...
<strong>};
</strong><strong>
</strong><strong>class lambertian : public material {
</strong>//...
};

class metal : public material {
//...
};

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
class dielectric : public material {
  private:
    // Refractive index in vacuum or air, or the ratio of the material's refractive index over
    // the refractive index of the enclosing media
    double refraction_index;

  public:
    dielectric(double refraction_index) : refraction_index(refraction_index) {}

    bool scatter(const ray&#x26; r_in, const hit_record&#x26; rec, color&#x26; attenuation, ray&#x26; scattered)
    const override {
        attenuation = color(1.0, 1.0, 1.0);
        double ri = rec.front_face ? (1.0/refraction_index) : refraction_index;

        vec3 unit_direction = unit_vector(r_in.direction());
        // refract vector
        vec3 refracted = refract(unit_direction, rec.normal, ri);

        scattered = ray(rec.p, refracted);
        return true;
    }
};
//&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;&#x3C;
</code></pre>

### T4.3 Update the scene (main.cpp)

Change material left from metal to dielectric  in main()

```cpp
void main() {
    hittable_list world;

    auto material_ground = make_shared<lambertian>(color(0.8, 0.8, 0.0));
    auto material_center = make_shared<lambertian>(color(0.1, 0.2, 0.5));
    auto material_left   = make_shared<dielectric>(1.50); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    auto material_right  = make_shared<metal>(color(0.8, 0.6, 0.2));
    
    //...
}
```

### Build and Run Your Program

If building is successful, run your program in the terminal as follows using your executable name

```
 ./ray01.exe > image11.ppm
```

It is slower then reflection as more refracted rays are involved.

If successful, you are going to see a lovely photo like the following

It is very interesting to notice that you see the sky colour through the bottom half of the glass sphere due to refraction.

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2FopvXM3ryYP9DMhXSOYRG%2Fe9bc2a5e-49f7-4974-aa46-c9bf8329adde.png?alt=media&#x26;token=b7e99f5b-aa55-457c-86ed-86ae03ffdb1c" alt="" width="563"><figcaption></figcaption></figure>

### T4.4 Add total internal reflection and Schlick Approximation in&#x20;

Revise the satter() method in class dielectric to add total internal reflection, and schlick approximation for Fresnel effect.

```cpp
class dielectric : public material {
  private:
    // Refractive index in vacuum or air, or the ratio of the material's refractive index over
    // the refractive index of the enclosing media
    double refraction_index;

    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    static double reflectance(double cosine, double refraction_index) {
        // Use Schlick's approximation for reflectance.
        auto r0 = (1 - refraction_index) / (1 + refraction_index);
        r0 = r0*r0;
        return r0 + (1-r0)*std::pow((1 - cosine),5);
    }
    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  public:
    dielectric(double refraction_index) : refraction_index(refraction_index) {}

    bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
    const override {
        attenuation = color(1.0, 1.0, 1.0);
        double ri = rec.front_face ? (1.0/refraction_index) : refraction_index;

        vec3 unit_direction = unit_vector(r_in.direction());
        vec3 refracted = refract(unit_direction, rec.normal, ri);

        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0);
        double sin_theta = std::sqrt(1.0 - cos_theta*cos_theta);

        bool cannot_refract = ri * sin_theta > 1.0;
        vec3 direction;

        if (cannot_refract)
            direction = reflect(unit_direction, rec.normal);
        else
            direction = refract(unit_direction, rec.normal, ri);

        if (cannot_refract || reflectance(cos_theta, ri) > random_double())
            direction = reflect(unit_direction, rec.normal);
        else
            direction = refract(unit_direction, rec.normal, ri);
        
        scattered = ray(rec.p, direction);
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        
        return true;
    }
};
```

#### Add new material in main() and add a hollow glass sphere

```cpp
    void main() {
    // ...
    auto material_ground = make_shared<lambertian>(color(0.8, 0.8, 0.0));
    auto material_center = make_shared<lambertian>(color(0.1, 0.2, 0.5));
    auto material_left   = make_shared<dielectric>(1.50); 
    auto material_bubble = make_shared<dielectric>(1.00 / 1.50); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    auto material_right  = make_shared<metal>(color(0.8, 0.6, 0.2));

    world.add(make_shared<sphere>(point3( 0.0, -100.5, -1.0), 100.0, material_ground));
    world.add(make_shared<sphere>(point3( 0.0,    0.0, -1.2),   0.5, material_center));
    world.add(make_shared<sphere>(point3(-1.0,    0.0, -1.0),   0.5, material_left));
    // >>>>>>>>>>>>>>>
    world.add(make_shared<sphere>(point3(-1.0,    0.0, -1.0),   0.4, material_bubble)); 
    // <<<<<<<<<<<<<<<
    world.add(make_shared<sphere>(point3( 1.0,    0.0, -1.0),   0.5, material_right));
    
    // ...
```

&#x20;You are going to see output similar to the following:

<figure><img src="https://3464970502-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3JUKGJZ67JX02QZdPhsy%2Fuploads%2FnXBtUou10UPa6xQ0qkLx%2F213deede-1265-41b0-a224-2b5fce9ebfa1.png?alt=media&#x26;token=965fff27-8d8b-4134-868d-b35f6ef2f940" alt="" width="563"><figcaption></figcaption></figure>
