私は楽しみのために純粋なPythonでパストレーシングを一緒にハッキングしてきましたが、以前のシェーディング(ランベルトの余弦則)があまりきれいではなかったので、再帰的なパストレーシングを実装しようとしています。
私のエンジンは中途半端な出力を出します:
私のパストレーシング関数は、次のように再帰的に定義されています。
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())