【问题标题】:Why isn't my pathtracing code working?为什么我的路径跟踪代码不起作用?
【发布时间】:2011-07-28 19:55:53
【问题描述】:

我一直在用纯 Python 编写一个路径跟踪器,只是为了好玩,而且由于我之前的着色东西不太漂亮 (Lambert's cosine law),我正在尝试实现递归路径跟踪。

我的引擎给出了一个异常的输出:

我的路径追踪函数是递归定义的,像这样:

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 < 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

而我的场景(我做了一个自定义的 XML 描述格式)是这样的:

<?xml version="1.0" ?>

<scene>
  <camera name="camera">
    <position x="0" y="-5" z="0" />
    <direction x="0" y="1" z="0" />

    <focalplane width="0.5" height="0.5" offset="1.0" pixeldensity="1600" />
  </camera>

  <objects>
    <sphere name="sphere1" radius="1.0">
      <material emittance="0.9" reflectance="0">
        <diffuse r="0.5" g="0.5" b="0.5" />
      </material>

      <position x="1" y="0" z="0" />
    </sphere>

    <sphere name="sphere2" radius="1.0">
      <material emittance="0.0" reflectance="0">
        <diffuse r="0.8" g="0.5" b="0.5" />
      </material>

      <position x="-1" y="0" z="0" />
    </sphere>
  </objects>
</scene>

我很确定我的引擎存在一些根本缺陷,但我就是找不到它......


这是我的新追踪功能:

def Trace(ray, scene, n):
  if n > 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 < 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) > 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.

我很确定路径跟踪代码可以正常工作,因为我手动投射了一些光线并获得了相当合理的结果。我(现在)遇到的问题是相机没有通过图像平面中的所有像素拍摄光线。我编写了这段代码来查找与像素相交的射线,但它无法正常工作:

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())

【问题讨论】:

    标签: python raytracing


    【解决方案1】:

    我的第一个问题是 if test &gt; result: 应该是 if test &lt; result: 吗?您正在寻找最近的命中,而不是最远的命中。

    其次,为什么要在n = Ray(ray.position(result) + direction * 0.00001, direction)这里的命中点上加上direction*0.00001?这会将你的新射线的起点放在球体内。我相信当你递归调用TracePath2时,你乘以的点积将是负数,这将有助于解释问题。

    编辑:更新问题

    这行让我很困惑:answer += TracePath2(n, scene, bounce_count + 1) * hit.diffuse * dp。首先answer 将只是Color(0.0, 0.0, 0.0),所以你可以简单地return racePath2(n, scene, bounce_count + 1) * hit.diffuse * dp。但这仍然困扰着我,因为我不明白你为什么将递归调用和hit.diffuse 相乘。这样的事情对我来说更有意义return racePath2(n, scene, bounce_count + 1) * dp + hit.diffuse。还有一件事,你永远不会检查bounce_count。无论如何,你永远不会在这个场景中永远递归,但如果你想渲染更大的场景,你会在开始时需要这样的东西if bounce_count &gt; 15: return black

    编辑 2:

    我看到我仍然想知道的一件事是最后的 if-else。首先,我不完全确定那部分代码在做什么。我认为您正在测试射线是否在物体内部。在这种情况下,您的测试将是这样的inside = normal.dot(ray.direction) &gt; 0.0。我只是针对normal 而不是direction 进行测试,因为在半球中使用随机方向可能会给出错误的答案。现在,如果你在物体里面,你想出去,但如果你已经出去了,你想留在外面?就像我说的,我不太确定那部分应该做什么。

    【讨论】:

    • 呸,我怎么会错过呢?我认为选择最近的十字路口会很好。我做了这些改变,事情变得有趣了:我右边的球体完全消失了,淹没了场景的背景。渲染完成后我会发布并更新。
    • 好的,现在 那个 故障已修复。事实证明,False 小于 any 数字,这会错误地将未命中触发为命中。 if test and test &lt; result: 解决了这个问题。我会立即发布(几乎相同的)更新。
    • @Blender 你试过我的新建议了吗?我想知道他们是否有帮助。
    • 哇,对不起。我没有收到有关编辑的通知...我已经重写了一些关键部分,我将使用带注释的代码更新我的问题。
    • 我从一个完全工作的路径跟踪器(用 JavaScript 编写:29a.ch/2010/5/17/path-tracing-a-cornell-box-in-javascript)实现了光线跟踪代码,所以我确信 部分有效。现在,我最大的问题是弄清楚为什么我的相机不能正确拍摄光线。我会发布代码,因为过去两天这部分让我疯狂...
    猜你喜欢
    • 2023-03-04
    • 2014-11-20
    • 2023-03-24
    • 2014-01-23
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    相关资源
    最近更新 更多