<p>The answer, I realized, is to specify a minimum stepsize inside refractive materials.</p>

CC BY-SA 4.0
2023-11-18T21:19:50.073Z
2023-11-18T21:19:50.073Z
Griffin S.
<p>I'm sorry if this question is imprecise, or violates any guidelines. This is my first post on Stack Exchange, so any feedback is welcome.</p>
<p>I am working on a ray-marcher in my spare time, which you can see for reference at <a href="https://replit.com/@GriffinSchreibe/My-First-Raymarcher" rel="nofollow noreferrer">https://replit.com/@GriffinSchreibe/My-First-Raymarcher</a> (Please press view code to see what's happening, as it does not output anything to the console you can see just from viewing the project). Yes, I am working on an online IDE, purely because I do not have sufficient compute power on my laptop.
My code is <strong>heavily</strong> based on Ray-tracing in one weekend. I have gradually worked through the first few sections, but one section has been stumping me, namely the refraction section.</p>
<p>In my code I have this function that returns the ray color, given a ray shot from the camera:</p>
<pre><code>vec3 ray_color(const ray &amp;r, int depth, float current_medium_ior = 1) {
  // A medium ior of 1 means air.
  if (depth &lt;= 0) {
    return vec3(0, 0, 0);
  }
  // setup parameters and variables
  float total_distance_traveled = 0.0;
  const int NUMBER_OF_STEPS = 64; // 32;
  const float MAXIMUM_HIT_DISTANCE = 0.001;
  const float MAXIMUM_TRACE_DISTANCE = 1000.0;

  vec3 color;

  // iterate through the max number of steps
  for (int i = 0; i &lt; NUMBER_OF_STEPS; ++i) {
    // update the position
    vec3 current_position =
        r.origin() + total_distance_traveled * r.direction();
    // find the distance
    Surface closest_surface = map_the_world(current_position);
    float distance_to_closest = step_size * closest_surface.sd;
    
    // if we hit something
    float abs_dist = abs(distance_to_closest);
    if (abs_dist &lt; MAXIMUM_HIT_DISTANCE) {
      vec3 normal = calculate_normal(current_position);
      vec3 direction;

      float new_medium_ior = closest_surface.ior;
      if (closest_surface.surface_type == &quot;REFRACTIVE&quot;) {

          // Check if the medium has changed
          if (new_medium_ior != current_medium_ior) {
            std::clog&lt;&lt;&quot;Medium change!\n&quot;;
              float ior_ratio = new_medium_ior / current_medium_ior;

              vec3 direction = refract(r.direction(), normal, ior_ratio);

              // Recursion
              color = ray_color(ray(current_position + 0.004 * direction, direction), depth - 1, current_medium_ior);
              return color;
          } else {
              // Continue with loop for further intersections within the medium or other behavior
          }
      }
 else {
        direction = closest_surface.scatter_direction(r.direction(), normal);
        // Catch degenerate scatter direction
        if (direction.near_zero())
          direction = normal;
        // Recursion.
        color = 0.5 * ray_color(ray(current_position + 0.004 * normal, direction), depth - 1, current_medium_ior);
        return color;
      }
    }
    
    // if we have been going for too long
    if (total_distance_traveled &gt; MAXIMUM_TRACE_DISTANCE) {
      break;
    }

    // update the distance
    total_distance_traveled += distance_to_closest;
  }

  // if we didn't find anything
  // then we do a blue-white gradient.
  vec3 unit_direction = unit_vector(r.direction());
  auto a = 0.5 * (unit_direction.y() + 1.0);
  return (1.0 - a) * vec3(1.0, 1.0, 1.0) + a * vec3(0.5, 0.7, 1.0);
}
</code></pre>
<p>As you can see, I attempt to implement refraction, but only when the medium changes. After weeks of toil, I am still perplexed as to how to properly implement ray-marched refraction in the spirit of Ray-tracing in one Weekend (By that I mean no &quot;hacky&quot; solutions).</p>
<p>I would be deeply grateful to anyone who could help me solve this problem.
Oh, and you can play around with the code in the link above.</p>
<p>EDIT:
After fixing the medium change check to see if the origin medium was different from the current medium, I am now getting the refracting sphere as blue.
I think that what is happening is that the step size is getting tiny inside the material, as I had the Distance Estimator return 0 inside the material. It then ran out of steps, and returned sky color.
I am still curious about the best way to stop the ray-marcher running out of steps, though.</p>

<raymarching><refraction>
Raymarching Refraction
Griffin S.
# Raymarching Refraction

(Asked by: username_0 on 2023-11-11)

I'm sorry if this question is imprecise, or violates any guidelines. This is my first post on Stack Exchange, so any feedback is welcome.

I am working on a ray-marcher in my spare time, which you can see for reference at <https://replit.com/@GriffinSchreibe/My-First-Raymarcher> (Please press view code to see what's happening, as it does not output anything to the console you can see just from viewing the project). Yes, I am working on an online IDE, purely because I do not have sufficient compute power on my laptop.
My code is **heavily** based on Ray-tracing in one weekend. I have gradually worked through the first few sections, but one section has been stumping me, namely the refraction section.

In my code I have this function that returns the ray color, given a ray shot from the camera:

```
vec3 ray_color(const ray &r, int depth, float current_medium_ior = 1) {
  // A medium ior of 1 means air.
  if (depth <= 0) {
    return vec3(0, 0, 0);
  }
  // setup parameters and variables
  float total_distance_traveled = 0.0;
  const int NUMBER_OF_STEPS = 64; // 32;
  const float MAXIMUM_HIT_DISTANCE = 0.001;
  const float MAXIMUM_TRACE_DISTANCE = 1000.0;

  vec3 color;

  // iterate through the max number of steps
  for (int i = 0; i < NUMBER_OF_STEPS; ++i) {
    // update the position
    vec3 current_position =
        r.origin() + total_distance_traveled * r.direction();
    // find the distance
    Surface closest_surface = map_the_world(current_position);
    float distance_to_closest = step_size * closest_surface.sd;
    
    // if we hit something
    float abs_dist = abs(distance_to_closest);
    if (abs_dist < MAXIMUM_HIT_DISTANCE) {
      vec3 normal = calculate_normal(current_position);
      vec3 direction;

      float new_medium_ior = closest_surface.ior;
      if (closest_surface.surface_type == "REFRACTIVE") {

          // Check if the medium has changed
          if (new_medium_ior != current_medium_ior) {
            std::clog<<"Medium change!\n";
              float ior_ratio = new_medium_ior / current_medium_ior;

              vec3 direction = refract(r.direction(), normal, ior_ratio);

              // Recursion
              color = ray_color(ray(current_position + 0.004 * direction, direction), depth - 1, current_medium_ior);
              return color;
          } else {
              // Continue with loop for further intersections within the medium or other behavior
          }
      }
 else {
        direction = closest_surface.scatter_direction(r.direction(), normal);
        // Catch degenerate scatter direction
        if (direction.near_zero())
          direction = normal;
        // Recursion.
        color = 0.5 * ray_color(ray(current_position + 0.004 * normal, direction), depth - 1, current_medium_ior);
        return color;
      }
    }
    
    // if we have been going for too long
    if (total_distance_traveled > MAXIMUM_TRACE_DISTANCE) {
      break;
    }

    // update the distance
    total_distance_traveled += distance_to_closest;
  }

  // if we didn't find anything
  // then we do a blue-white gradient.
  vec3 unit_direction = unit_vector(r.direction());
  auto a = 0.5 * (unit_direction.y() + 1.0);
  return (1.0 - a) * vec3(1.0, 1.0, 1.0) + a * vec3(0.5, 0.7, 1.0);
}

```

As you can see, I attempt to implement refraction, but only when the medium changes. After weeks of toil, I am still perplexed as to how to properly implement ray-marched refraction in the spirit of Ray-tracing in one Weekend (By that I mean no "hacky" solutions).

I would be deeply grateful to anyone who could help me solve this problem.
Oh, and you can play around with the code in the link above.

EDIT:
After fixing the medium change check to see if the origin medium was different from the current medium, I am now getting the refracting sphere as blue.
I think that what is happening is that the step size is getting tiny inside the material, as I had the Distance Estimator return 0 inside the material. It then ran out of steps, and returned sky color.
I am still curious about the best way to stop the ray-marcher running out of steps, though.

**Tags:** raymarching, refraction


---

## 1 answers

### Answer by username_0 on 2023-11-18. Score: 0 | Accepted answer

The answer, I realized, is to specify a minimum stepsize inside refractive materials.


---

