我一直在用纯Python编写一个pathtracer,只是为了好玩,而且由于我之前的着色并不太漂亮(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描述格式)是:
^{pr2}$我很确定我的引擎有一些根本的缺陷,但我就是找不到它。。。在
这是我新的ish跟踪函数:
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())
我的第一个问题是
if test > result:
应该是if test < result:
?你要找的是最近的,不是最远的。在第二,为什么要把
direction*0.00001
加到n = Ray(ray.position(result) + direction * 0.00001, direction)
的命中点上?这会让你的新光线进入球体。我相信当你递归地调用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 > 15: return black
。在编辑2:
我看到的唯一一件事,我仍然在想,如果不是在最后。首先,我不太清楚这部分代码在做什么。我想你在测试光线是否在物体内部。在这种情况下,您的测试将是这样的
inside = normal.dot(ray.direction) > 0.0
。我只是针对normal
而不是direction
进行测试,因为在半球中使用随机方向可能会给出错误的答案。现在,如果你在你想出去的物体里面,但是如果你已经出去了你想呆在外面?就像我说的,我不太确定那部分应该做什么。在相关问题 更多 >
编程相关推荐