Non recursive ray_color function from Raytracing in One Weekend in GLSL using compute shaders
Asked Answered
H

1

0

I am trying to write a real time ray tracer using compute shaders in opengl 4.3. I know that this is a rather popular question. I have checked this, and this, but the architecture provided over there does not really correspond to my use case.

I am simply trying to transform the ray_color function provided in the P. Shirley's book here into a non recursive function.

The mentioned ray_color function:

color ray_color(const ray& r, const hittable& world, int depth) {
    hit_record rec;

    // If we've exceeded the ray bounce limit, no more light is gathered.
    if (depth <= 0)
        return color(0,0,0);

    if (world.hit(r, 0.001, infinity, rec)) {
        point3 target = rec.p + rec.normal + random_unit_vector();
        return 0.5 * ray_color(ray(rec.p, target - rec.p), world, depth-1);
    }

    vec3 unit_direction = unit_vector(r.direction());
    auto t = 0.5*(unit_direction.y() + 1.0);
    return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}

Here is my attempt:


vec3 no_hit_color(in Ray r) {
  vec3 dir = normalize(r.direction);
  float temp = 0.5 * (dir.y + 1.0);
  vec3 cval = vec3(1.0 - temp) + temp * vec3(0.5, 0.7, 1.0);
  return cval;
}

vec3 ray_color(in Ray r, in Scene scene, int depth) {
  //
  Ray r_in;
  r_in.origin = r.origin;
  r_in.direction = r.direction;
  vec3 bcolor = vec3(1);

  while (true) {
    Ray r_out;
    if (depth <= 0) {
      //
      return vec3(0);
    }
    HitRecord rec;
    if (hit_scene(scene, r_in, 0.001, INFINITY, rec)) {
      vec3 target = rec.point + random_in_hemisphere(rec.normal);
      r_in = makeRay(rec.point, target - rec.point);
      depth--;
      bcolor *= 0.5;
    } else {
      bcolor *= no_hit_color(r_in);
      return bcolor;
    }
  }
}

If I use a static value for depth, with something like #define MAX_DEPTH, I think i can implement the algorithm, by making my own stack, but I would like to keep the depth as dynamic variable where I can let users tweak according to their computing power. So I would like to implement it using while if possible. My version produces a black slice near the bottom of the sphere, which does not correspond to the reference picture.

Update 1:

I am slightly convinced that the above implementation is correct but my camera position from which I am generating rays is problematic.

Hanway answered 27/5, 2020 at 8:2 Comment(0)
H
1

I have confirmed that the implementation is indeed correct. Here is a glsl version and c++ version for future reference. It should give you a direction for implementing more complex stuff later on.

// glsl version
vec3 ray_color(in Ray r, in Scene scene, int depth) {
  //
  Ray r_in;
  r_in.origin = r.origin;
  r_in.direction = r.direction;
  vec3 bcolor = vec3(1);

  while (true) {
    if (depth <= 0) {
      //
      return vec3(0);
      // return bcolor;
    }
    HitRecord rec;
    if (hit_scene(scene, r_in, 0.001, INFINITY, rec)) {
      vec3 target = rec.point + random_in_hemisphere(rec.normal);
      r_in = makeRay(rec.point, target - rec.point);
      depth--;
      bcolor *= 0.5;
    } else {
      vec3 dir = normalize(r_in.direction);
      float temp = 0.5 * (dir.y + 1.0);
      bcolor *= vec3(1.0 - temp) + temp * vec3(0.5, 0.7, 1.0);
      return bcolor;
    }
  }
}
// cpp version
color ray_color2(const Ray &r, const HittableList &scene, int depth) {
  Ray r_in = Ray(r.origin, r.direction);
  color rcolor = color(1);
  while (true) {
    HitRecord record;
    if (depth <= 0) {
      // final case
      return color(0);
    }
    if (scene.hit(r_in, 0.001, INF, record)) {
      // recursive case
      point3 target = record.point + random_in_hemisphere(record.normal);
      r_in = Ray(record.point, target - record.point);
      depth--;
      rcolor *= 0.5;
    } else {
      vec3 direction = to_unit(r_in.direction);
      double temp = 0.5 * (direction.y + 1.0);
      rcolor *= (1.0 - temp) * color(1.0) + temp * color(0.5, 0.7, 1.0);
      return rcolor;
    }
  }
}

Basically as long as the contribution of rays can be modeled with a linear operator, it should be possible to use while loop to implement the function. Notice that the function does not use a call stack, thus it can be used where the maximum bounce rate, or maximum depth whichever you prefer, for rays is dynamic.

Hanway answered 28/5, 2020 at 5:36 Comment(3)
I think if (depth <= 0) { return vec3(0); } should return the bcolor instead otherwise if you hit the depth limit black artifacts would occur ...Vacuva
@Vacuva you are correct, but the same problem also exists in the original code used in the book as well if I am not mistaken, and my goal was to produce non recursive version of the code in the book. You are free to modify it in anyway you wish.Hanway
Besides I have not had the chance to test this code on a lot of settings. I am fairly sure your remark would be quite valid for scenes with lots of metalic surface. But the function should give the general idea.Hanway

© 2022 - 2024 — McMap. All rights reserved.