Почему мой код трассировки не работает? - PullRequest
1 голос
/ 04 апреля 2011

Я взламывал pathtracer на чистом Python, просто для забавы, и поскольку моя предыдущая вещь с затенением не была слишком красивой ( закон косинусов Ламберта ), я пытаюсь реализовать рекурсивный трассировка пути.

Мой двигатель выдает аварийный вывод:

enter image description here

Моя функция отслеживания пути определяется рекурсивно, например:

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

1 Ответ

4 голосов
/ 04 апреля 2011

Мой первый вопрос: 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, потому что использование случайного направления в полушарии может дать неправильный ответ.Теперь, если вы находитесь внутри объекта, который хотите выйти, но хотите остаться вне дома, если вы уже вне дома?Как я уже сказал, я не совсем уверен, что эта часть должна делать.

...