title: Why isn't my pathtracing code working?
body: <p>I've been hacking together a pathtracer in pure Python, just for fun, and since my previous shading-thing wasn't too pretty (<a href="http://en.wikipedia.org/wiki/Lambert%27s_cosine_law" rel="nofollow noreferrer">Lambert's cosine law</a>), I'm trying to implement recursive pathtracing.</p>
  
  <p>My engine gives an abortive output:</p>
  
  <p><img src="https://i.stack.imgur.com/LRMAX.png" alt="enter image description here"></p>
  
  <p>My pathtracing function is defined recursively, like this:</p>
  
  <pre><code>def TracePath2(ray, scene, bounce_count):
    result = 100000.0
    hit = False
  
    answer = Color(0.0, 0.0, 0.0)
  
    for object in scene.objects:
      test = object.intersection(ray)
  
      if test and test &lt; result:
        result = test
        hit = object
  
    if not hit:
      return answer
  
    if hit.emittance:
      return hit.diffuse * hit.emittance
  
    if hit.diffuse:
      direction = RandomDirectionInHemisphere(hit.normal(ray.position(result)))
  
      n = Ray(ray.position(result), direction)
      dp = direction.dot(hit.normal(ray.position(result)))
      answer += TracePath2(n, scene, bounce_count + 1) * hit.diffuse * dp
  
    return answer
  </code></pre>
  
  <p>And my scene (I made a custom XML description format) is this:</p>
  
  <pre><code>&lt;?xml version="1.0" ?&gt;
  
  &lt;scene&gt;
    &lt;camera name="camera"&gt;
      &lt;position x="0" y="-5" z="0" /&gt;
      &lt;direction x="0" y="1" z="0" /&gt;
  
      &lt;focalplane width="0.5" height="0.5" offset="1.0" pixeldensity="1600" /&gt;
    &lt;/camera&gt;
  
    &lt;objects&gt;
      &lt;sphere name="sphere1" radius="1.0"&gt;
        &lt;material emittance="0.9" reflectance="0"&gt;
          &lt;diffuse r="0.5" g="0.5" b="0.5" /&gt;
        &lt;/material&gt;
  
        &lt;position x="1" y="0" z="0" /&gt;
      &lt;/sphere&gt;
  
      &lt;sphere name="sphere2" radius="1.0"&gt;
        &lt;material emittance="0.0" reflectance="0"&gt;
          &lt;diffuse r="0.8" g="0.5" b="0.5" /&gt;
        &lt;/material&gt;
  
        &lt;position x="-1" y="0" z="0" /&gt;
      &lt;/sphere&gt;
    &lt;/objects&gt;
  &lt;/scene&gt;
  </code></pre>
  
  <p>I'm pretty sure that there's some fundamental flaw in my engine, but I just can't find it...</p>
  
  <hr>
  
  <p>Here's my new-ish tracing function:</p>
  
  <pre><code>def Trace(ray, scene, n):
    if n &gt; 10: # Max raydepth of 10. In my scene, the max should be around 4, since there are only a few objects to bounce off, but I agree, there should be a cap.
      return Color(0.0, 0.0, 0.0)
  
    result = 1000000.0 # It's close to infinity...
    hit = False
  
    for object in scene.objects:
      test = object.intersection(ray)
  
      if test and test &lt; result:
        result = test
        hit = object
  
    if not hit:
      return Color(0.0, 0.0, 0.0)
  
    point = ray.position(result)
  
    normal = hit.normal(point)
    direction = RandomNormalInHemisphere(normal) # I won't post that code, but rest assured, it *does* work.
  
    if direction.dot(ray.direction) &gt; 0.0:
      point = ray.origin + ray.direction * (result + 0.0000001) # We're going inside an object (for use when tracing glass), so move a tad bit inside to prevent floating-point errors.
    else:
      point = ray.origin + ray.direction * (result - 0.0000001) # We're bouncing off. Move away from surface a little bit for same reason.
  
    newray = Ray(point, direction)
  
    return Trace(newray, scene, n + 1) * hit.diffuse + Color(hit.emittance, hit.emittance, hit.emittance) # Haven't implemented colored lights, so it's a shade of gray for now.
  </code></pre>
  
  <hr>
  
  <p>I'm pretty sure that the pathtracing code works, as I manually casted some rays and got pretty legitimate results. The problem I'm having (now) is that the camera doesn't shoot rays through <em>all</em> the pixels in the image plane. I made this code to find the ray intersecting a pixel, but it's not working properly:</p>
  
  <pre><code>origin = scene.camera.pos                 # + 0.5 because it      # 
                                            # puts the ray in the   # This calculates the width of one "unit"
                                            # *middle* of the pixel # 
  worldX = scene.camera.focalplane.width - (x + 0.5)                * (2 * scene.camera.focalplane.width / scene.camera.focalplane.canvasWidth)
  worldY = scene.camera.pos.y - scene.camera.focalplane.offset # Offset of the imaging plane is know, and it's normal to the camera's direction (directly along the Y-axis in this case).
  worldZ = scene.camera.focalplane.height - (y + 0.5)               * (2 * scene.camera.focalplane.height / scene.camera.focalplane.canvasHeight)
  
  ray = Ray(origin, (scene.camera.pos + Point(worldX, worldY, worldZ)).norm())
  </code></pre>