3

I've been following Ray Tracing In One Weekend in Javascript and have been doing well until it was time to implement diffuse lighting. After a lot of debugging, the output it produced didn't match the expected output.

Expected Result

My Result

The diffuse lighting cuts off sharply, almost resembling reflections instead? I have no clue what has cause this to happen; perhaps there's something wrong with how new rays are calculated?

Replit Code

Here are some of the most important chunks of code:

This is a recursive function that calculates colour output from rays

function rayColour(r, world, depth) {
  if (depth <= 0) {
    return new Colour(0, 0, 0);
  }

  var rec = world.hit(r, 0, infinity, rec);
  if (rec[0]) {
    var target = rec[1].p.add(rec[1].normal).add(randomInUnitSphere());
    return rayColour(new Ray(rec[1].p, target.subtract(rec[1].p)), world, depth-1).multiply(0.5);
  }
  var unitDirection = normalize(r.dir);
  var t = 0.5 * (unitDirection.y + 1);
  return new Colour(1, 1, 1).multiply(1-t).add(new Colour(0.5, 0.7, 1).multiply(t));
}

Main rendering function for a pixel

for(var p = 0; p < ppt; p++) {
    var i = curPixel % WIDTH;
    var j = HEIGHT-1 - Math.floor(curPixel / WIDTH);

    var pixelColour = new Colour(0, 0, 0);

    for (var s = 0; s < samplesPerPixel; ++s) {
      var u = (i + Math.random()) / (WIDTH-1);
      var v = (j + Math.random()) / (HEIGHT-1);
      var r = cam.getRay(u, v);
      pixelColour = pixelColour.add(rayColour(r, world, maxDepth));
    }

    putPixel(i, HEIGHT-1-j, pixelColour, samplesPerPixel);
    curPixel++;
  }

Sphere Ray collision

hit(r, tMin, tMax, rec) {
    var oc = r.origin().subtract(this.center);
    var a = r.dir.lengthSquared();
    var halfb = dot(oc, r.dir);
    var c = oc.lengthSquared() - this.radius*this.radius;
    
    var discriminant = halfb*halfb - a*c;
    if (discriminant < 0) return [false, rec];
    var sqrtd = Math.sqrt(discriminant);

    // find nearest collision
    var root = (-halfb - sqrtd) / a;
    if (root < tMin || tMax < root) {
      root = (-halfb + sqrtd) / a;
      if (root < tMin || tMax < root) return [false, rec];
    }

    rec.t = root;
    rec.p = r.at(rec.t);
    var outwardNormal = (rec.p.subtract(this.center)).divide(this.radius);
    rec.setFaceNormal(r, outwardNormal);

    return [true, rec];
  }

Collision for all physical objects

hit(r, tMin, tMax, rec) {
    var tempRec = new hitRecord();
    var hitAnything = false;
    var closestSoFar = tMax;

    for (var i in this.list) {
      var tempRec = this.list[i].hit(r, tMin, closestSoFar, tempRec)
      if (tempRec[0]) {
        hitAnything = true;
        closestSoFar = tempRec[1].t;
        rec = tempRec[1];
      }
      tempRec = new hitRecord();
    }

    return [hitAnything, rec];
  }

Any help would be appreciated!

LemonHead
  • 31
  • 2

1 Answers1

0

Turns out the function I was using to generate random points in a sphere was completely broken; only outputting (0, 0, 0) or (1, 1, 1). Fixing that and shadow acne has brought me to the intended result!

LemonHead
  • 31
  • 2