<p>You need to make use of a discriminant, also when dealing with floating point comparisons it is wise to use an epsilon (small value for accommodating fp-error).</p>
<p>Also keep in mind that it matters whether the surface normal is pointing along the ray, or against it.</p>
<p>Below is a function which I use in my own raytracer, also based on the same book. It is written in C and refracts a given vector3. You would assign the return value to your <code>ray.direction</code> in your case.</p>
<pre class="lang-c prettyprint-override"><code>// vector*vector means dot product here

#define EPSILON = 0.000001f

static inline vec3 refract(
    vec3 dir, // direction of incoming light ray
    vec3 n, // surface normal of refracting surface, pointing outwards (so roughly in the against the direction of light ray)
    float ni_over_nt // refractive indices fraction
){
    float dt = dir*n; // dot prod.
    float discriminant = 1.f - ni_over_nt*ni_over_nt*(1.f-dt*dt);
    if (discriminant &gt; EPSILON){
        return (dir - n*dt)*ni_over_nt - n*sqrtf(discriminant);
    } else {
        // total internal reflection
        // reflect the direction against the surface normal
        return reflect(direction, normal);
    }
}
</code></pre>
<p>Usage of the function is as follows:</p>
<pre class="lang-c prettyprint-override"><code>if (direction*normal) &gt; EPSILON) // dot prod. of ray dir. and surf. normal
{
    // ray is entering the object
    nOut = -normal; 
    ni_over_nt = mat-&gt;IOR; // surface material index of refraction.
} else {
    // ray is exiting the object
    nOut = normal;
    ni_over_nt = 1.f/mat-&gt;IOR;
}

vec3 refracted = refract(direction, nOut, ni_over_nt);
// this is your refracted ray direction
</code></pre>
<p>Added bonus: reflection function used in the refraction:</p>
<pre class="lang-c prettyprint-override"><code>static inline vec3 reflect3(vec3 v, vec3 n){
    // reflect a vector v off of a plane with normal n
    return  v -n*2*(v*n); //v*n = dot prod.
}
</code></pre>

CC BY-SA 4.0
2020-11-14T12:00:56.107Z
One thing that slightly confuses me here is your comments for when the ray is entering and exiting the object... My understanding is that when the dot product between the ray's direction and the surface normal is positive then the normal is pointing with the ray and the ray is therefore inside the sphere and will be exiting the sphere after the intersection.
Eoin Roe
CC BY-SA 4.0
2020-11-15T13:30:58.383Z
Oh, I might have flipped those comments on accident. A lot of 3d engines invert the normals, as does mine. But in theory you're right.
AnnoyinC
CC BY-SA 4.0
2020-11-10T20:18:43.787Z
2020-11-10T20:35:13.820Z
2020-11-10T20:35:13.820Z
AnnoyinC
<p>I have been working on recreating some of the scenes from Peter Shirley's raytracing books using compute shaders and the new Metal raytracing API.  It is going well but I am having some trouble with the glass material.</p>
<p>I've read Pete's blog post on this - <a href="http://psgraphics.blogspot.com/2020/04/deugging-refraction-in-sphre.html" rel="nofollow noreferrer">http://psgraphics.blogspot.com/2020/04/deugging-refraction-in-sphre.html</a></p>
<p>But I am still unsure as to what is the cause of the issue that you can see in the pictures I have attached.<a href="https://i.sstatic.net/y1H8E.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/y1H8E.jpg" alt="enter image description here" /></a></p>
<p>The glass appears grainy and you get an effect similar to z-fighting when you move the camera around.</p>
<p><a href="https://i.sstatic.net/K7cVN.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/K7cVN.jpg" alt="enter image description here" /></a></p>
<p>This is the code for the glass material:</p>
<pre><code>else if (instances.materials[instanceIndex] == glass)
{
    bool front_face = dot(normalize(ray.direction), worldSpaceSurfaceNormal) &lt; 0.0 ? true : false;
            
    float kEtaRatio = front_face ? kEtaAir / kEtaGlass
                                 : kEtaGlass / kEtaAir;
          
    ray.origin = worldSpaceIntersectionPoint;
    ray.direction = refract(normalize(ray.direction), worldSpaceSurfaceNormal, kEtaRatio);
}
</code></pre>
<p>I have implemented all the code for the full material with Schlick approximation etc, however it appears that it is this first step shown here that is causing the issue.</p>
<p>I am only using one sample per pixel at the moment but I'm unsure why that would only cause problems with the glass and not the other materials?</p>
<p>Any advice would be greatly appreciated.</p>
<p><a href="https://i.sstatic.net/syI0w.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/syI0w.jpg" alt="enter image description here" /></a></p>

CC BY-SA 4.0
2020-11-10T20:04:31.853Z
Did you make sure that the refraction ray is spawned somewhat inside of the sphere so that it does not re-intersect with the same sphere? Are you using epsilon values for floating point comparisons?
AnnoyinC
CC BY-SA 4.0
2020-11-11T21:07:36.037Z
This was actually originally one of the problems I was having with the refraction. I was adding `worldSpaceSurfaceNormal * 1e-3f` to the worldSpaceIntersectionPoint when updating the ray origin to avoid re-intersecting the same triangle again.  This worked well for the diffuse and metallic materials but failed completely for the glass.  I just realised after your comment about it being spawned inside of the sphere that I should instead subtract this epsilon value and now it is working great!
Eoin Roe
CC BY-SA 4.0
2020-11-12T00:03:12.070Z
Yup! Small errors like that will get you, as well as off-by-ones. One of the first things I do is flip fractions, change signs, etc when something doesn't work. Not very analytical but very pragmatic.
AnnoyinC
<raytracing><refraction>
Debugging refraction in a sphere
Eoin Roe
# Debugging refraction in a sphere

(Asked by: username_0 on 2020-11-09)

I have been working on recreating some of the scenes from Peter Shirley's raytracing books using compute shaders and the new Metal raytracing API. It is going well but I am having some trouble with the glass material.

I've read Pete's blog post on this - <http://psgraphics.blogspot.com/2020/04/deugging-refraction-in-sphre.html>

But I am still unsure as to what is the cause of the issue that you can see in the pictures I have attached.[![enter image description here](https://i.sstatic.net/y1H8E.jpg)](https://i.sstatic.net/y1H8E.jpg)

The glass appears grainy and you get an effect similar to z-fighting when you move the camera around.

[![enter image description here](https://i.sstatic.net/K7cVN.jpg)](https://i.sstatic.net/K7cVN.jpg)

This is the code for the glass material:

```
else if (instances.materials[instanceIndex] == glass)
{
    bool front_face = dot(normalize(ray.direction), worldSpaceSurfaceNormal) < 0.0 ? true : false;
            
    float kEtaRatio = front_face ? kEtaAir / kEtaGlass
                                 : kEtaGlass / kEtaAir;
          
    ray.origin = worldSpaceIntersectionPoint;
    ray.direction = refract(normalize(ray.direction), worldSpaceSurfaceNormal, kEtaRatio);
}

```

I have implemented all the code for the full material with Schlick approximation etc, however it appears that it is this first step shown here that is causing the issue.

I am only using one sample per pixel at the moment but I'm unsure why that would only cause problems with the glass and not the other materials?

Any advice would be greatly appreciated.

[![enter image description here](https://i.sstatic.net/syI0w.jpg)](https://i.sstatic.net/syI0w.jpg)

**Tags:** raytracing, refraction

> **Comments:**
> **username_1**: Did you make sure that the refraction ray is spawned somewhat inside of the sphere so that it does not re-intersect with the same sphere? Are you using epsilon values for floating point comparisons?  
> **username_0**: This was actually originally one of the problems I was having with the refraction. I was adding `worldSpaceSurfaceNormal * 1e-3f` to the worldSpaceIntersectionPoint when updating the ray origin to avoid re-intersecting the same triangle again.  This worked well for the diffuse and metallic materials but failed completely for the glass.  I just realised after your comment about it being spawned inside of the sphere that I should instead subtract this epsilon value and now it is working great!  
> **username_1**: Yup! Small errors like that will get you, as well as off-by-ones. One of the first things I do is flip fractions, change signs, etc when something doesn't work. Not very analytical but very pragmatic.  

---

## 1 answers

### Answer by username_1 on 2020-11-10. Score: 1

You need to make use of a discriminant, also when dealing with floating point comparisons it is wise to use an epsilon (small value for accommodating fp-error).

Also keep in mind that it matters whether the surface normal is pointing along the ray, or against it.

Below is a function which I use in my own raytracer, also based on the same book. It is written in C and refracts a given vector3. You would assign the return value to your `ray.direction` in your case.

```c
// vector*vector means dot product here

#define EPSILON = 0.000001f

static inline vec3 refract(
    vec3 dir, // direction of incoming light ray
    vec3 n, // surface normal of refracting surface, pointing outwards (so roughly in the against the direction of light ray)
    float ni_over_nt // refractive indices fraction
){
    float dt = dir*n; // dot prod.
    float discriminant = 1.f - ni_over_nt*ni_over_nt*(1.f-dt*dt);
    if (discriminant > EPSILON){
        return (dir - n*dt)*ni_over_nt - n*sqrtf(discriminant);
    } else {
        // total internal reflection
        // reflect the direction against the surface normal
        return reflect(direction, normal);
    }
}

```

Usage of the function is as follows:

```c
if (direction*normal) > EPSILON) // dot prod. of ray dir. and surf. normal
{
    // ray is entering the object
    nOut = -normal; 
    ni_over_nt = mat->IOR; // surface material index of refraction.
} else {
    // ray is exiting the object
    nOut = normal;
    ni_over_nt = 1.f/mat->IOR;
}

vec3 refracted = refract(direction, nOut, ni_over_nt);
// this is your refracted ray direction

```

Added bonus: reflection function used in the refraction:

```c
static inline vec3 reflect3(vec3 v, vec3 n){
    // reflect a vector v off of a plane with normal n
    return  v -n*2*(v*n); //v*n = dot prod.
}

```

> **Comments:**
> **username_0**: One thing that slightly confuses me here is your comments for when the ray is entering and exiting the object... My understanding is that when the dot product between the ray's direction and the surface normal is positive then the normal is pointing with the ray and the ray is therefore inside the sphere and will be exiting the sphere after the intersection.  
> **username_1**: Oh, I might have flipped those comments on accident. A lot of 3d engines invert the normals, as does mine. But in theory you're right.  

---

