Как бороться с преломлением, когда лучи начинаются внутри вложенного объекта

Я создаю простой трассировщик лучей для образовательных целей и хочу добавить преломление к объектам. Используя закон Снеллиуса, я могу рекурсивно создать новый луч в точках пересечения. В настоящее время трассировщик лучей поддерживает только сферы, и я использую сцену, в которой у меня есть несколько сфер, вложенных друг в друга с разными индексами преломления.

Если я запускаю луч снаружи сфер, все кажется простым. Вы начинаете с показателя преломления сцены, и как только вы наткнетесь на первую сферу, преломите луч, используя предыдущий показатель преломления и показатель преломления материала сферы, пока не наткнетесь на следующую сферу и так далее. Используя нормали пересечения, я могу определить, вхожу ли я в сферу или выхожу из нее.

Однако я не понимаю, как мне обращаться с листьями сферы и что делать, если луч не начинается во внешней части сцены.

  • Могу ли я просто взять стопку показателей преломления и подняться на один уровень выше, как только покину сферу?
  • Как я могу определить, с каким показателем преломления я должен начать, если я начну внутри сфер?

Пример

У вас есть три сферы с показателями преломления 0,9, 1,1 и 0,8 от внешнего к внутреннему. Воздушный индекс 1,0

  • Ваша камера находится за пределами сферы и направлена ​​в центр сферы:

    • start index is 1.0, you first hit the outer sphere with index 0.9 and refract from 1.0 to 0.9 and save that your ray is now in 0.9 material
    • вы попадаете в среднюю сферу и замечаете постоянную материала 1,1, так как вы сохранили 0,9, вы знаете, что вам нужно преломить от 0,9 до 1,1 и сохранить 1,1 в дополнение к 0,9
    • вы попадаете во внутреннюю сферу и преломляете от 1,1 до 0,8, и у вас есть сохранение до сих пор 0,9, 1,1 и 0,8
    • вы снова нажимаете на внутреннюю сферу (на этот раз вы выходите из нее, поэтому вы проверяете свои сохраненные значения и знаете, что вам нужно переключиться обратно на 1.1)
    • ... пока не окажешься снаружи
  • Проблема сейчас, когда камера находится внутри сферы. Вы не будете знать, на какой показатель преломления вам нужно переключиться.


person Etan    schedule 04.10.2010    source источник
comment
Просто обратите внимание, что преломление должно быть больше >=1, чтобы уравнения в моем ответе ниже были действительными. То есть, если вы не заинтересованы в метаматериалах трассировки лучей :)   -  person Daniel Farrell    schedule 14.08.2012


Ответы (2)


Публикую это с точки зрения физики, а не с точки зрения реализации трассировки лучей: P.

Закон Снеллиуса гласит, что отношение синусов угла падения и угла преломления равно обратному отношению показателей преломления двух сред по обе стороны от границы.

Таким образом, когда у вас есть луч, приближающийся к новому материалу, и вы хотите знать угол в новом материале, вам нужно знать угол, под которым луч падает на новый материал, показатель преломления нового материала и показатель преломления материала, в котором в данный момент находится луч.

Поскольку вы говорите, что преломление прекрасно работает при перемещении в сферу, вы уже должны знать показатель преломления каждой сферы и вашей сцены.

Я бы сказал, что создание стека показателей преломления было бы хорошим способом справиться с набором вложенных материалов, так как вам придется касаться всех показателей преломления, которые вы снова вставляете в стек, когда вы выходите. вложенного набора сфер.

Что касается определения, с каким показателем преломления вы должны начать, покидая сферы, вы всегда говорите: sin(theta1)/sin(theta2) = [показатель преломления 2]/[показатель преломления 1]. Таким образом, вам нужен показатель преломления материала, в котором вы сейчас находитесь, и показатель преломления материала, к которому вы собираетесь двигаться.

Извините, если я неправильно понял ваш вопрос, но я надеюсь, что это поможет!

person johnw188    schedule 04.10.2010
comment
Да, но что делать, если ваша камера находится внутри вложенных материалов, а вы смотрите оттуда снаружи? ;-) - person Etan; 05.10.2010

У меня есть похожий трассировщик лучей (написанный на Python) и я наткнулся на ту же проблему: чтобы правильно отработать физику, нужно знать показатель преломления с каждой стороны границы пересечения. Это заняло довольно много времени, чтобы решить элегантно, но в конце концов я выбрал это решение/дизайн:

Дизайн

1) Сцена. У меня есть основной объект сцены (в основном массив всех объектов сцены), у вас, вероятно, будет что-то подобное. Он хранит геометрические объекты.

Методы:

  • intersection_points(ray) - возвращает список всех точек пересечения, отсортированных по расстоянию от луча.
  • intersection_objects(ray) - возвращает список всех объектов пересечения, отсортированных по расстоянию от луча.
  • containing_object(ray) — возвращает объект, содержащий луч.
  • objects() - возвращает список всех объектов в произвольном порядке.

Примечание. Сцена добавляет в список дополнительный объект: Scene_Boundary. Это гигантский ящик (или Сфера), который заключает в себе всю сцену, т.е. ВСЕ находится внутри этой границы.

2) Объекты. Сделайте так, чтобы геометрические объекты (например, ваша сфера) реализовывали эти методы.

Методы:

  • contains(ray) — возвращает True, если начало луча находится внутри объекта, False, если на поверхности, и False, если снаружи
  • ray_is_on_surface(ray) - возвращает True, если луч находится только на поверхности, иначе False.
  • intersection_points(ray) — возвращает точку (точки) пересечения луча с объектом.
  • surface_normal(ray) - возвращает вектор нормали к поверхности, на которую упал луч (это поможет с френелевским отражением и преломлением)

Для оптических расчетов объекты также должны иметь показатель преломления.

Переменные экземпляра:

  • refractive_index

Граничная задача

Задача, которую мы хотим решить: каков показатель преломления внутри (n1) и снаружи (n2) границы? Для этого мы следуем этой процедуре:

1) Проследите луч через всю сцену:

sphere # origin = (0,0,0), radius = 1
ray  # origin = (0,0,0), direction = (0,0,1) Note: the ray is inside the sphere
scene.add_object(sphere)
ipoints = scene.intersection_points(ray) #  [ (0,0,1), (0,0,10) ]
iobjects = scene.intersection_objects(ray) # [ Sphere, Scene_Boundary]

Помните, что они отсортированы по расстоянию от начала луча. Последний элемент в ipoints и iobjects — это пересечение луча с границей сцены. Мы воспользуемся этим позже!

2) n1 находится просто путем нахождения содержащего объекта, например:

obj1 = scene.containing_object(ray) # Scene_Boundary
n1 = obj1.refractive_index() # n1 = 1. Scene_Boundary always has refractive index of Air

3) n2 находится путем поиска на один объект вперед в списке iobject, например. в псевдокоде:

index = iobjects.index_of_object(obj1)
obj2 = iobjects[index+1]
n2 = obj2.refractive_index() # n2 = 1.5 e.g. Glass

4) Получить нормаль поверхности для последующего использования:

normal = obj1.surface_normal(ray)

У вас есть вся информация, необходимая для расчета правильного отражения и преломления. Это достаточно общее правило, чтобы работать, даже если луч находится вне объекта, но иногда мне нужно было реализовать некоторую логическую фильтрацию, чтобы сделать алгоритм более надежным, но это в основном все!

Отражение и преломление

Вы можете отразить вектор, просто зная нормаль поверхности. В Python с использованием numpy я делаю это так:

def reflect_vector(normal, vector):
   d = numpy.dot(normal, vector)
   return vector - 2 * d * normal

Преломление (как обсуждалось) требует значений n1 и n2:

def fresnel_refraction(normal, vector, n1, n2):
    n = n1/n2
    dot = np.dot(norm(vector), norm(normal))
    c = np.sqrt(1 - n**2 * (1 - dot**2))
    sign = 1
    if dot < 0.0:
        sign = -1
    refraction = n * vector + sign*(c - sign*n*dot) * normal
    return norm(refraction)

Наконец, вам нужно будет рассчитать коэффициент отражения для луча, где угол — это угол между направлением луча и нормалью к поверхности (предполагается, что ваш луч «неполяризованный»). Сравните это со случайным числом от 0 до 1, чтобы решить, происходит ли отражение.

def fresnel_reflection(angle, n1, n2):
    assert 0.0 <= angle <= 0.5*np.pi, "The incident angle must be between 0 and 90 degrees to calculate Fresnel reflection."
    # Catch TIR case
    if n2 < n1:
        if angle > np.arcsin(n2/n1):
            return 1.0

    Rs1 = n1 * np.cos(angle) - n2 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2)
    Rs2 = n1 * np.cos(angle) + n2 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2)
    Rs = (Rs1/Rs2)**2
    Rp1 = n1 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2) - n2 * np.cos(angle)
    Rp2 = n1 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2) + n2 * np.cos(angle)
    Rp = (Rp1/Rp2)**2
    return 0.5 * (Rs + Rp)

Заключительные комментарии

Все это взято из моего проекта оптической трассировки лучей на Python, который еще не выпущен (!), но вы можете проверить некоторые подробности здесь: http://daniel.farrell.name/freebies/pvtrace. Мне нравится Питон! Здесь перечислены несколько проектов трассировки лучей Python, http://groups.google.com/group/python-ray-tracing-community/web/list-of-python-statistical-ray-tracers. . Наконец, будьте осторожны с дробными показателями преломления в вашем примере, уравнение не сработает.

Обновить

Скриншот, на котором это реализовано в моем трассировщике лучей, доступен по адресу http://github.com/danieljfarrell/pvtrace < img src="https://i.stack.imgur.com/FVFPA.jpg" alt="альтернативный текст">

person Daniel Farrell    schedule 03.01.2011