Методы трассировки лучей (Ray Tracing ) на сегодняшний день считаются наиболее мощными и универсальными методами создания реалистичных изображений. Известно много примеров реализации алгоритмов трассировки для качественного отображения самых сложных трехмерных сцен. Можно отметить, что универсальность методов трассировки взначительной мере обусловлена тем, что в их основе лежат простые и ясные понятия, которые отражают наш опыт восприятия окружающего мира.
Рис. 8.12. Модели отражения: а – идеальное зеркало, б - неидеальное зеркало, в – диффузное, г – сумма диффузного и зеркального, д – обратное, е - сумма диффузного, зеркального и лбратного
Как мы видим окружающую реальность? Во-первых, нужно определиться с тем, что мы вообще способны видеть. Это изучается в специальных дисциплинах, а до некоторой степени, это вопрос философский. Но здесь мы будем считать, что окружающие объекты обладают такими свойствами относительно света:
излучают;
отражают и поглощают;
пропускают сквозь себя.
Рис. 8.13. Излучение – а – раномерно во все тороны, б - направленно
Каждое из этих свойств можно описать некоторым набором характеристик. Например, излучение можно охарактеризовать интенсивностью, направленностью, спектром. Излучение может исходить от условно точечного источника (далекая звезда) или от источника рассеянного света (скажем, от расплавленной лавы, извергающейся из кратера вулкана). Распространение излучения может осуществляться вдоль довольно узкого луча (сфокусированный луч лазера) или конусом (прожектор), или равномерно во все стороны (Солнце), или еще как-то. Свойство отражения (поглощение) можно описать характеристиками диффузного рассеивания и зеркального отражения. Прозрачность можно описать ослаблением интенсивности и преломлением.
Распределение световой энергии по возможным направлениям световых лучей можно отобразить с помощью векторных диаграмм, в которых длина векторов соответствует интенсивности (рис. 8.12 – 8.14).
В предшествующих параграфах мы с вами уже ознакомились с видами отражения, которые упоминаются наиболее часто - зеркальным и диффузным. Реже в литературе поминается обратное зеркальное или антизеркальное от ражение, в котором максимум интенсивности отражения соответствует направлению на источник. Обратное зеркальное отражение имеют некоторые виды растительности на поверхности Земли, наблюдаемые с высоты рисовые поля.
Два крайних, идеализированных случая преломления изображены на рис. 8.13.
Некоторые реальные объекты преломляют лучи намного более сложным образом, например, обледеневшее стекло.
Один и тот же объект реальной действительности может восприниматься как источник света, а может, при ином рассмотрении, считаться предметом, только отражающим и пропускающим свет. Например, купол облачного неба в некоторой трехмерной сцене может моделироваться в видепротяженного (распределенного) источника света, а в других моделях это же небо выступает как полупрозрачная среда, освещенная со стороны Солнца.
Рис. 8.14. Преломление а – идеальное, б - дифузное
В общем случае каждый объект описывается некоторым сочетанием вышеперечисленных трех свойств. В качестве упражнения попробуйте привести пример объекта, который обладает одновременно тремя указанными свойствами - сам излучает свет и, в то же время, отражает, а также пропускает свет от других источников. Вероятно, ваше воображение подскажет и другие примеры, нежели, скажем, раскаленное докрасна стекло.
Теперь рассмотрим то, как формируется изображение некоторой сцены, которая содержит несколько пространственных объектов. Будем считать, что из точек поверхности (объема) излучаемых объектов выходят лучи света. Можно назвать такие лучи первичными -они освещают все другое.
Важным моментом является предположение, что световой луч в свободном пространстве распространяется вдоль прямой линии (хотя в специальных разделах физики изучаются также и причины возможного искривления). Но вгеометрической оптике принято, что луч света распространяется прямолинейно до тех пор, пока не встретится отражающая поверхность или граница среды преломления. Так будем полагать и мы.
От источников излучения исходит по разным направлениям бесчисленное множество первичных лучей (даже луч лазера невозможно идеально сфокусировать - все равно свет будет распространяться не одной идеально тонкой линией, а конусом, пучком лучей). Некоторые лучи уходят в свободное пространство, а некоторые (их также бесчисленное множество) попадают на другие объекты. Если луч попадет в прозрачный объект, то, преломляясь, он идет дальше, при этом некоторая часть световой энергии поглощается. Подобно этому, если на пути луча встречается зеркально отражающая поверхность, то он также изменяет направление, а часть световой энергиипоглощается. Если объект зеркальный и одновременно прозрачный (например, обычное стекло), то будут уже два луча - в этом случае говорят, что луч расщепляется.
Можно сказать, что в результате воздействия на объекты первичных лучей возникают вторичные лучи. Бесчисленное множество вторичных лучей уходит в свободное пространство, но некоторые из них попадают на другие объекты. Так, многократноотражаясь и преломляясь, отдельные световые лучи приходят в точку наблюдения - глаз человека или оптическую систему камеры. Очевидно, что в точку наблюдения может попасть и часть первичных лучей непосредственно от источников излучения. Такимобразом, изображение сцены формируется некоторым множеством световых лучей.
Цвет отдельных точек изображения определяется спектром и интенсивностью первичных лучей источников излучения, а также поглощением световой энергии в объектах, встретившихся на пути соответствующих лучей.
Рис. 8.15. Схема обратной трассировки лучей
Непосредственная реализация данной лучевой модели формирования изображения представляется затруднительной. Можно попробовать разработать алгоритм построения изображения указанным способом. В таком алгоритме необходимо предусмотреть перебор всех первичных лучей и определить, какие из них попадают в объекты и в камеру. Потом выполнить перебор всех вторичных лучей, и также учесть только те, которые попадают в объекты и в камеру. И так далее. Можно назвать такой метод прямой трассировкой лучей. Практическая ценность такого метода вызовет сомнение. В самом деле, как учитывать бесконечное множество лучей, идущих во все стороны? Очевидно, что полный перебор бесконечного числа лучей в принципе невозможен. Даже если каким-то образом свести это к конечному числу операций (например, разделить всю сферу направлений на угловые секторы и оперировать уже не бесконечно тонкими линиями, а секторами), все равно остается главный недостаток метода - много лишних операций, связанных с расчетом лучей, которые потом не используются. Так, во всяком случае, это представляется в настоящее время.
Метод обратной трассировки лучей позволяет значительно сократить перебор световых лучей. Метод разработан в 80-х годах, основополагающими считаются работы Уитте-да и Кэя . Согласно этому методу отслеживание лучей осуществляется не от источников света, а в обратном"направлении - от точки наблюдения. Так учитываются только те лучи, которые вносят вклад в формирование изображения.
Рассмотрим, как можно получить растровое изображение некоторой трехмерной сцены методом обратной трассировки. Предположим, что плоскость проецирования разбита на множество квадратиков - пикселов. Выберем центральную проекцию с центром схода на некотором расстоянии от плоскости проецирования. Проведем прямую линию из центра схода через середину квадратика (пиксела) плоскости проецирования (рис. 8.15). Это будетпервичный луч обратной трассировки. Если прямая линия этого луча попадает в один или несколько объектов сцены, то выбираем ближайшую точку пересечения. Для определения цвета пиксела изображения нужно учитывать свойства объекта, а также то, какое световое излучение приходится на соответствующую точку объекта.
Рис. 8.16. Обратная трассировка для объектов, имеющих свойства зеркального отражения и преломления
Если объект зеркальный (хотя бы частично), то строим вторичный луч - луч падения, считая лучом отражения предыдущий, первичный, трассируемый луч. Выше мы рассматривали зеркальное отражение и получили формулы для вектора отраженного луча по заданным векторам нормали и луча падения. Но здесь нам известен вектор отраженного луча, а как найти вектор падающего луча? Для этого можно использовать ту же формулу зеркального отражения, но определяя необходимый вектор луча падения как отраженный луч. То есть отражение наоборот.
Для идеального зеркала достаточно потом проследить лишь очередную точку пересечения вторичного луча с некоторым объектом. Что означает термин "идеальное зеркало"? Будем считать, что такое зеркало имеет идеально равную отполированную поверхность, поэтому одному отраженному лучу соответствует только один падающий луч. Зеркало может быть затемненным, то есть поглощать часть световой энергии, но все равно выполняется правило: один луч падает - один отражается. Можно рассматривать также "неидеальное зеркало".Это будет означать, что поверхность неровная. Направлению отраженного луча будут соответствовать несколько падающих лучей (или наоборот, один падающий луч порождает несколько отраженных лучей), которые образуют некоторый конус, возможно, несимметричный, с осью вдоль линии падающего луча идеального зеркала. Конус соответствует некоторому закону распределения интенсивностей, простейший из которых описывается моделью Фонга - косинус угла, возведенный в некоторую степень. Неидеальное зеркало резко усложняет трассировку - нужно проследить не один, а множество падающих лучей, учитывать взнос излучения от других видимых из данной точки объектов.
Если объект прозрачный, то необходимо построить новый луч, такой, который при преломлении давал бы предшествующий трассируемый луч. Здесь также можно воспользоваться обратимостью, которая справедлива и для преломления. Для расчета вектора искомого луча можно применить рассмотренные выше формулы для вектора луча преломления,считая, что преломление происходит в обратном направлении (рис. 8.16).
Если объект обладает свойствами диффузного отражения и преломления, то, в общем случае, как и для неидеального зеркала, необходимо трассировать лучи, которые приходятот всех имеющихся объектов. Для диффузного отражения интенсивность отраженного света, как известно, пропорциональна косинусу угла между вектором луча от источника света и нормалью. Здесь источником света может выступать любой видимый из данной точки объект, способный передавать световую энергию.
Если выясняется, что текущий луч обратной трассировки не пересекает любой объект, а направляется в свободное пространство, то на этом трассировка для этого луча заканчивается.
Обратная трассировка лучей в том виде, в котором мы ее здесь рассмотрели, хотя и сокращает перебор, но не позволяет избавиться от бесконечного числа анализируемых лучей.В самом деле, данный метод позволяет сразу получить для каждой точки изображения один первичный луч обратной трассировки. Однако вторичных лучей отражения уже может быть бесконечное число. Так, например, если объект может отражать свет от любого другого объекта, и если эти другие объекты имеют довольно большие размеры, то какие именно точкиизлучающих объектов нужно учитывать для построения соответствующих лучей, например, при диффузном отражении? Очевидно, все точки.
При практической реализации метода обратной трассировки вводят ограничения. Некоторые из них необходимы, чтобы можно было в принципе решить задачу синтеза изображения, а некоторые ограничения позволяют значительно повысить быстродействие трассировки. Примеры таких ограничений.
1. Среди всех типов объектов выделяются некоторые, которые назовем источниками света. Источники света могут только излучать свет, но не могут его отражать или преломлять (будем рассматривать толькоточечные источники света).
2. Свойства отражающих поверхностей описываются суммой двух компонентов - диффузного и зеркального.
3. В свою очередь, зеркальность также описывается двумя составляющими. Первая (reflection ) учитывает отражение от других объектов, которые не являются источниками света. Строится только один зеркально отраженный лучr для дальнейшей трассировки.Вторая составляющая ( Specular ) означает световые блики от источников света. Для этого направляются лучи на все источники света и определяются углы, образованные этими лучами с зеркально отраженным лучом обратной трассировки(r ). При зеркальном отражении цвет точки поверхности определяется цветом того, что отражается. В простейшем случае зеркало не имеет собственного цвета поверхности.
4. При диффузном отражении учитываются только лучи от источников света. Лучи от зеркально отражающих поверхностей игнорируются. Если луч, направленный на данный источник света, закрывается другим объектом, значит, данная точка объекта находится в тени. При диффузном отражении цвет освещенной точки поверхности определяется собственным цветом поверхности и цветом источников света.
5. Для прозрачных (1гап5рагеп() объектов обычно не учитывается зависимость коэффициента преломления от длины волны. Иногда прозрачность вообще моделируют без преломления, то есть направление преломленного луча I совпадает с направлением падающего луча.
Для учета освещенности объектов светом, который рассеивается другими объектами, вводится фоновая составляющая(ат bient ).
7. Для завершения трассировки вводят некоторое предельное значение освещенности, которое уже не должно вносить взнос в результирующий цвет, или ограничивают количество итераций.
Согласно модели Уиттеда цвет некоторой точки объекта определяется суммарной интенсивностью
I( ) = KaIa( )C( ) + KdId( )C( ) + KsIs( ) + KrIr( ) + KtIt( )
где λ - длина волны,
С (λ) - заданный исходный цвет точки объекта,
К а,K d ,K s ,K r и К t - коэффициенты, учитывающие свойства конкретного объекта через параметры фонового подсвечивания, диффузного рассеивания, зеркальности, отражения и прозрачности,
I a - интенсивность фонового подсвечивания,
I d - интенсивность, учитываемая для диффузного рассеивания,
I s - интенсивность, учитываемая для зеркальности,
I r - интенсивность излучения, приходящего по отраженному лучу,
I t - интенсивность излучения, приходящего по преломленному лучу.
Интенсивность фонового подсвечивания (1 а ) для некоторого объекта обычно константа. Запишем формулы для других интенсивностей. Для диффузного отражения
I
d
=
где I i (λ) - интенсивность излученияi - ro источника света, θ i - угол между нормалью к поверхности объекта и направлением наi - vi источник света.
Для зеркальности:
I
d
=
где р - показатель степени от единицы до нескольких сотен (согласно модели Фонга),α i -угол между отраженным лучом (обратной трассировки) и направлением на г"-й источник света.
Интенсивности излучений проходящих по отраженному лучу (I r ), а так же по преломленному лучу (I t ) , умножают на коэффициент, учитывающий ослабление интенсивности в зависимости от расстояния, пройденного лучом. Такой коэффициент записывается в виде е - d где d - пройденное расстояние, – параметр ослабления, учитывающий свойства среды, в которой распространяется луч.
Для первичного луча необходимо задать направление, которое соответствует избранной проекции. Если проекция центральная, то первичные лучи расходятся из общей точки, для параллельной проекции первичные лучи - параллельные. Луч можно задать, например, координатами начальной и конечной точек отрезка, координатой начальной точки и направлением, или еще как-нибудь.Задание первичного луча однозначно определяет проекцию изображаемой сцены . При обратной трассировке лучей любые преобразования координат вообще не обязательны. Проекция получается автоматически - в том числе, нетолько плоская, но и, например, цилиндрическая или сферическая. Это одно из проявлений универсальности метода трассировки.
В ходе трассировки лучей необходимо определять точки пересечения прямой линии луча с объектами. Способ определения точки пересечения зависит от того, кокой это объект, и каким образом он представлен в определенной графической системе. Так, например, для объектов, представленных в виде многогранников и полигональных сеток, можно использовать известные методы определения точки пересечения прямой и плоскости, рассмотренные в аналитической геометрии. Однако, если ставится задача определения пересечения луча с гранью, то необходимо еще, чтобы найденная точка пересечения лежала внутри контура грани.
Известно несколько способов проверки произвольной точки на принадлежность полигону. Рассмотрим две разновидности, в сущности, одного и того же метода (рис. 8.17).
Первый способ. Находятся все точки пересечения контура горизонталью, которая соответствует координатеYзаданной точки. Точки пересечения сортируются по возрастанию значений координат Х. Пары точек пересечения образуют отрезки. Если точка, которая проверяется, принадлежит одному из отрезков (для этого сравниваются координаты Х заданной точки и концов отрезков), то она – внутренняя.
Рис. 8.17. Точка – внутренняя, если: а - точка принадлежит секущему отрезку, б – число пересечений нечетное
Второй способ. Определяется точка, лежащая на одной горизонтали с испытуемой точкой, причем требуется, чтобы она лежала вне контура полигона. Найденная внешняя точка и испытуемая являются концами горизонтального отрезка. Определяются точки пересечения данного отрезка с контуром полигона. Если количество пересечений нечетное, это значит, что испытуемая точка – внутренняя.
Если луч пересекает несколько объектов, то выбирается ближайшая точка по направлению текущего луча.
Сделаем общие выводы о относительно метода обратной трассировки лучей.
Положительные черты
1. Универсальность метода, его применимость для синтеза изображения довольно сложных пространственных схем. Воплощает много законов геометрической оптики. Просто реализуются разнообразные проекции.
2. Даже усеченные варианты данного метода позволяют получить довольно реалистичные изображения. Например если ограничится только первичными лучами(из точки проецирования), то это дает удаление невидимых точек. Трассировка уже одного – двух вторичных лучей дает тени, зеркальность, прозрачность.
3. Все преобразования координат (если таковые имеются) линейные, поэтому довольно просто работать с текстурами.
4. Для одного пиксела растрового изображения можно трассировать несколько близко расположенных лучей, а потом усреднять их цвет для устранения лестничного (ступенчатого) эффекта (антиалиасинг).
5. Поскольку расчет отдельной точки изображения выполняется независимо от других точек, то это может быть эффективно использовано при реализации данного метода в параллельных вычислительных системах, в которых лучи могут трассироваться одновременно.
Недостатки
1. Проблемы с моделированием диффузного отражения и преломления
2. Для каждой точки изображения необходимо выполнять много вычислительных операций. Трассировка лучей принадлежит к числу самых медленных алгоритмов синтеза изображений.
Я знаю, это немного разочаровывает. Где отражения, тени и красивый внешний вид? Мы всё это получим, ведь мы пока только начали. Но это хорошее начало - сферы выглядят как круги, а это лучше, чем если бы они выглядели как кошки. Они не выглядят как сферы потому, что мы упустили важный компонент, позволяющий человеку определять форму объекта - то, как он взаимодействует со светом.
Мы начнём с некоторых упрощающих допущений, которые облегчат нам жизнь.
Во-первых, мы объявим, что всё освещение имеет белый цвет. Это позволит нам охарактеризовать любой источник освещения единственным действительным числом i, называемым яркостью освещения. Симуляция цветного освещения не так сложна (необходимо только три значения яркости, по одному на канал, и вычисление всех цветов и освещения поканально), но чтобы сделать нашу работу проще, я не буду его делать.
Во-вторых, мы избавимся от атмосферы. Это значит, что освещение не становятся менее яркими, независимо от их дальности. Затухание яркости света в зависимости от расстояния реализовать тоже не слишком сложно, но для ясности мы пока его пропустим.
Лампа накаливания - хороший пример из реального мира того, приближением чего является точечный источник освещения. Хотя лампа накаливания не испускает свет из одной точки и он не является совершенно всенаправленным, но приближение достаточно хорошее.
Давайте зададим вектор как направление из точки P в сцене к источнику освещения Q. Этот вектор, называемый световым вектором , просто равен . Заметьте, что поскольку Q фиксирована, а P может быть любой точкой сцены, то в общем случае будет разным для каждой точки сцены.
Это хитрый вопрос, и ответ зависит от того, что вы хотите отрендерить.
В масштабах Солнечной системы Солнце можно приблизительно считать точечным источником. В конце концов, оно испускает свет из точки (хотя и довольно большой) и испускает его во всех направлениях, то есть подходит под оба требования.
Однако если в вашей сцене действие происходит на Земле, то это не слишком хорошее приближение. Солнце находится так далеко, что каждый луч света будет на самом деле иметь одинаковое направление (Примечание: эта аппроксимация сохраняется в масштабе города, но не на более дальних расстояниях - на самом деле. древние греки смогли с удивительной точностью вычислить радиус Земли на основании разных направлений солнечного света в различных местах.). Хотя это можно аппроксимировать это с помощью точечного источника, сильно удалённого от сцены, это расстояние и расстояние между объектами в сцене настолько отличаются по величине, что могут появиться ошибки точности чисел.
Для таких случаев мы зададим направленные источники освещения . Как и точечные источники, направленный источник имеет яркость, но в отличие от них, у него нет позиции. Вместо неё у него есть направление . Можно воспринимать его как бесконечно удалённый точечный источник, светящий в определённом направлении.
В случае точечных источников нам нужно вычислять новый световой вектор для каждой точки P сцены, но в этом случае задан. В сцене с Солнцем и Землёй будет равен .
Представьте, что происходит на Луне. Единственным значимым источником освещения поблизости является Солнце. То есть «передняя половина» Луны относительно Солнца получает всё освещение, а «задняя половина» находится в полной темноте. Мы видим это с разных углов на Земле, и этот эффект создаёт то, что мы называем «фазами» Луны.
Однако ситуация на Земле немного отличается. Даже точки, не получающие освещения непосредственно от источника освещения, не находятся полностью в темноте (просто посмотрите на пол под столом). Как лучи света достигают этих точек, если «обзор» на источники освещения чем-то перекрыт?
Как я упомянул в разделе Цветовые модели , когда свет падает на объект, часть его поглощается, но остальная часть рассеивается в сцене. Это значит, что свет может поступать не только от источников освещения, но и от других объектов, получающих его от источников освещения и рассеивающих его обратно. Но зачем останавливаться на этом? Рассеянное освещение в свою очередь падает на какой-нибудь другой объект, часть его поглощается, а часть снова рассеивается в сцене. При каждом отражении свет теряет часть своей яркости, но теоретически можно продолжать ad infinitum (Примечание: на самом деле нет, потому что свет имеет квантовую природу, но достаточно близко к этому.).
Это значит, что нужно считать источником освещения каждый объект . Как можно представить, это сильно увеличивает сложность нашей модели, поэтому мы не пойдём таким путём (Примечание: но вы можете хотя бы загуглить Global Illumination и посмотреть на прекрасные изображения.).
Но мы всё равно не хотим, чтобы каждый объект был или освещён напрямую, или был полностью тёмным (если только мы не рендерим модель Солнечной системы). Чтобы преодолеть эту преграду, мы зададим третий тип источников освещения, называемый окружающим освещением , которое характеризуется только яркостью. Считается, что оно носит безусловный вклад освещения в каждую точку сцены. Это очень сильное упрощение чрезвычайно сложного взаимодействия между источниками освещения и поверхностями сцены, но оно работает.
Для вычисления освещённости точки нам просто нужно вычислить количество света, вносимое каждым источником и сложить их, чтобы получить одно число, представляющее общее количество полученного точкой освещения. Затем мы можем умножить цвет поверхности в этой точке на это число, чтобы получить правильно освещённый цвет.
Итак, что произойдёт, когда луч света с направлением из направленного или точечного источника падает на точку P какого-нибудь объекта в нашей сцене?
Интуитивно мы можем разбить объекты на два общих класса, в зависимости от того, как они ведут себя со светом: «матовые» и «блестящие». Поскольку большинство окружающих нас предметов можно считать «матовыми», то с них мы и начнём.
Чтобы убедиться в этом, внимательно посмотрите на какой-нибудь матовый объект, например, на стену: если двигаться вдоль стены, её цвет не меняется. То есть, видимый вами свет, отражённый от объекта, одинаков вне зависимости от того, в какое место объекта вы смотрите.
С другой стороны, количество отражённого света зависит от угла между лучом света и поверхностью. Интуитивно это понятно - энергия, переносимая лучом, в зависимости от угла должна распределиться по меньшей или большей поверхности, то есть энергия на единицу площади, отражённая в сцену, будет соответственно выше или ниже:
Чтобы выразить это математически, давайте охарактеризуем ориентацию поверхности по её вектору нормали . Вектор нормали, или просто «нормаль» - это вектор, перпендикулярный поверхности в какой-то точке. Также он является единичным вектором, то есть его длина равна 1. Мы будем называть этот вектор .
Для геометрической аналогии давайте представим яркость света как «ширину» луча. Его энергия распределяется по поверхности размером . Когда и имеют одно направление, то есть луч перпендикулярен поверхности, , а это значит, что энергия, отражённая на единицу площади равна падающей энергии на единицу площади; < . С другой стороны, когда угол между и приближается к , приближается к , то есть энергия на единицу площади приближается к 0; . Но что происходит в промежутках?
Ситуация отображена на схеме ниже. Мы знаем , и ; я добавил углы и , а также точки , и , чтобы сделать связанные с этой схемой записи проще.
Поскольку технически луч света не имеет ширины, поэтому мы будем считать, что всё происходит на бесконечно малом плоском участке поверхности. Даже если это поверхность сферы, то рассматриваемая область настолько бесконечно мала, что она почти плоская относительно размера сферы, так же как Земля выглядит плоской при малых масштабах.
Луч света с шириной падает на поверхность в точке под углом . Нормаль в точке равна , а энергия, переносимая лучом, распределяется по . Нам нужно вычислить .
Один из углов равен , а другой - . Тогда третий угол равен . Но нужно заметить, что и тоже образуют прямой угол, то есть тоже должны быть . Следовательно, :
Давайте рассмотрим треугольник . Его углы равны , и . Сторона равна , а сторона равна .
И теперь… тригонометрия спешит на помощь! По определению ; заменяем на , а на , и получаем
Заметьте, что при углах больше значение становится отрицательным. Если мы не задумываясь используем это значение, то в результате получим источники света, вычитающие свет. Это не имеет никакого физического смысла; угол больше просто означает, что свет на самом деле достигает задней части поверхности, и не вносит свой вклад в освещение освещаемой точки. То есть если становится отрицательным, то мы считаем его равным .
Этот вопрос намного хитрее, чем кажется, как мы увидим во второй части статьи. К счастью, для разбираемого нами случая есть очень простое решение: вектор нормали любой точки сферы лежит на прямой, проходящей через центр сферы. То есть если центр сферы - это , то направление нормали в точки равно :
Почему я написал «направление нормали», а не «нормаль»? Кроме перпендикулярности к поверхности, нормаль должна быть единичным вектором; это было бы справедливо, если бы радиус сферы был равен , что не всегда верно. Для вычисления самой нормали нам нужно разделить вектор на его длину, получив таким образом длину :
Light {
type = ambient
intensity = 0.2
}
light {
type = point
intensity = 0.6
position = (2, 1, 0)
}
light {
type = directional
intensity = 0.2
direction = (1, 4, 4)
}
Заметьте, что яркость удобно суммируется в , потому что из уравнения освещения следует, что никакая точка не может иметь яркость света выше, чем единица. Это значит, что у нас не получатся области со «слишком большой выдержкой».
Уравнение освещения довольно просто преобразовать в псевдокод:
ComputeLighting(P, N) {
i = 0.0
for light in scene.Lights {
if light.type == ambient {
i += light.intensity
} else {
if light.type == point
L = light.position - P
else
L = light.direction
n_dot_l = dot(N, L)
if n_dot_l > 0
i += light.intensity*n_dot_l/(length(N)*length(L))
}
}
return i
}
И единственное, что осталось - использовать ComputeLighting в TraceRay . Мы заменим строку, возвращающую цвет сферы
Return closest_sphere.color
на этот фрагмент:
P = O + closest_t*D # вычисление пересечения
N = P - closest_sphere.center # вычисление нормали сферы в точке пересечения
N = N / length(N)
return closest_sphere.color*ComputeLighting(P, N)
Просто ради интереса давайте добавим большую жёлтую сферу:
Sphere {
color = (255, 255, 0) # Yellow
center = (0, -5001, 0)
radius = 5000
}
Мы запускаем рендерер, и узрите - сферы наконец начали выглядеть как сферы!
Но постойте, как большая жёлтая сфера превратилась в плоский жёлтый пол?
Этого и не было, просто она настолько велика относительно других трёх, а камера настолько к ней близка, что она выглядит плоской. Так же, как наша планета выглядит плоской, когда мы стоим на её поверхности.
Возьмём бильярдный шар или только что вымытый автомобиль. В таких объектах проявляется особый шаблон распространения света, обычно с яркими областями, которые как будто движутся, когда вы ходите вокруг них. В отличие от матовых объектов, то, как вы воспринимаете поверхность этих объектов, на самом деле зависит от точки обзора.
Заметьте, что красные бильярдные шары остаются красными, если вы отойдёте на пару шагов назад, но яркое белое пятно, дающее им «блестящий» вид, похоже, двигается. Это значит, что новый эффект не заменяет диффузное отражение, а дополняет его.
Почему это происходит? Мы можем начать с того, почему это не происходит на матовых объектах. Как мы видели в предыдущем разделе, когда луч света падает на поверхнось матового объекта, он равномерно рассеивается назад в сцену во всех направлениях. Интуитивно понятно, что так происходит из-за неровности поверхности объекта, то есть на микроскопическом уровне она похожа на множество мелких поверхностей, направленных в случайных направлениях:
Но что будет, если поверхность не настолько неровная? Давайте возьмём другую крайность - идеально отполированное зеркало. Когда луч света падает на зеркало, он отражается в единственном направлении, которое симметрично углу падения относительно нормали зеркала. Если мы назовём направление отражённого света и условимся, что указывает на источник света, то получим такую ситуацию:
В зависимости от степени «отполированности» поверхности, она более или менее похожа на зеркало; то есть мы получаем «зеркальное» отражение (specular reflection, от латинского «speculum», то есть «зеркало»).
Для идеально отполированного зеркала падающий луч света отражается в единственном направлении . Именно это позволяет нам чётко видеть объекты в зеркале: для каждого падающего луча есть единственный отражённый луч . Но не каждый объект отполирован идеально; хотя бОльшая часть света отражается в направлении , часть его отражается в направлениях, близких к ; чем ближе к , тем больше света отражается в этом направлении. «Блеск» объекта определяет то, насколько быстро отражённый свет уменьшается при отдалении от :
Нас интересует то, как выяснить, какое количество света от отражается обратно в направлении нашей точки обзора (потому что это свет, который мы используем для определения цвета каждой точки). Если - это «вектор обзора», указывающий из в камеру, а - угол между и , то вот, что мы имеем:
При отражается весь свет. При свет не отражается. Как и в случае с диффузным отражением, нам нужно математическое выражение для определения того, что происходит при промежуточных значениях .
Давайте возьмём . У него есть хорошие свойства: , , а значения постепенно уменьшаются от до по очень красивой кривой:
Соответствует всем требованиям к функции «зеркального» отражения, так почему бы не использовать его?
Но нам не хватает ещё одной детали. В такой формулировке все объекты блестят одинаково. Как изменить уравнение для получения различных степеней блеска?
Не забывайте, что этот блеск - мера того, насколько быстро функция отражения уменьшается при увеличении . Очень простой способ получения различных кривых блеска заключается в вычислении степени некоего положительного показателя . Поскольку , то очевидно, что ; то есть ведёт себя точкно так же, как , только «уже». Вот для разных значений :
Чем больше значение , тем «уже» становится функция в окрестностях , и тем более блестящим выглядит объект.
Обычно называют показателем отражения , и он является свойством поверхности. Поскольку модель не основана на физической реальности, значения можно определить только методом проб и ошибок, то есть настраивая значения до тех пор, пока они не начнут выглядеть «естественно» (Примечание: для использования модели на основе физики см. двулучевую функцию отражательной способности (ДФОС)).
Давайте объединим всё вместе. Луч падает на поверхность в точке , где нормаль равна , а показатель отражения - . Какое количество света отразится в направлении обзора ?
Мы уже решили, что это значение равно , где - это угол между и , который в свою очередь является , отражённым относительно . То есть первым шагом будет вычисление из и .
Мы можем разложить на два вектора и , таких, что , где параллелен , а перпендикулярен :
Это проекция на ; по свойствам скалярного произведения и исходя из того, что , длина этой проекции равна . Мы определили, что будет параллелен , поэтому .
Поскольку , мы можем сразу получить .
Теперь посмотрим на ; поскольку он симметричен относительно , его компонент, параллельный , тот же, что и у , а перпендикулярный компонент противоположен компоненту ; то есть :
Подставляя полученные ранее выражения, мы получим
Sphere {
center = (0, -1, 3)
radius = 1
color = (255, 0, 0) # Красный
specular = 500 # Блестящий
}
sphere {
center = (-2, 1, 3)
radius = 1
color = (0, 0, 255) # Синий
specular = 500 # Блестящий
}
sphere {
center = (2, 1, 3)
radius = 1
color = (0, 255, 0) # Зелёный
specular = 10 # Немного блестящий
}
sphere {
color = (255, 255, 0) # Жёлтый
center = (0, -5001, 0)
radius = 5000
specular = 1000 # Очень блестящий
}
В коде нам нужно изменить ComputeLighting , чтобы он при необходимости вычислял значение «зеркальности» и прибавлял его к общему освещению. Заметьте, что теперь ему требуются и :
ComputeLighting(P, N, V, s) {
i = 0.0
for light in scene.Lights {
if light.type == ambient {
i += light.intensity
} else {
if light.type == point
L = light.position - P
else
L = light.direction
# Диффузность
n_dot_l = dot(N, L)
if n_dot_l > 0
i += light.intensity*n_dot_l/(length(N)*length(L))
# Зеркальность
if s != -1 {
R = 2*N*dot(N, L) - L
r_dot_v = dot(R, V)
if r_dot_v >
И наконец нам нужно изменить TraceRay , чтобы он передавал новые параметры ComputeLighting . очевиден; он берётся из данных сферы. Но как насчёт ? - это вектор, указывающий от объекта в камеру. К счастью, в TraceRay у нас уже есть вектор, направленный из камеры к объекту - это , направление трассируемого луча! То есть - это просто .
Вот новый код TraceRay с «зеркальным» отражением:
TraceRay(O, D, t_min, t_max) {
closest_t = inf
closest_sphere = NULL
for sphere in scene.Spheres {
t1, t2 = IntersectRaySphere(O, D, sphere)
if t1 in and t1 < closest_t
closest_t = t1
closest_sphere = sphere
if t2 in and t2 < closest_t
closest_t = t2
closest_sphere = sphere
}
if closest_sphere == NULL
return BACKGROUND_COLOR
P = O + closest_t*D # Вычисление пересечения
N = P - closest_sphere.center # Вычисление нормали сферы в точке пересечения
N = N / length(N)
return closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular)
}
И вот наша награда за всё это жонглирование векторами:
Давайте начнём с более фундаментального вопроса. Почему должны быть тени? Тени появляются там, где есть свет, но его лучи не могут достичь объекта, потому что на их пути есть другой объект.
Вы заметите, что в предыдущем разделе нас интересовали углы и вектора, но мы рассматривали только источник света и точку, которую нам нужно раскрасить, и полностью игнорировали всё остальное, что происходит в сцене - например, попавшийся на пути объект.
Вместо этого нам нужно добавить немного логики, говорящей "если между точкой и источником есть объект, то не нужно добавлять освещение, поступающее от этого источника ".
Мы хотим выделить два следующих случая:
Похоже, что у нас есть все необходимые для этого инструменты.
Давайте начнём с направленного источника. Мы знаем ; это точка, которая нас интересует. Мы знаем ; это часть определения источника освещения. Имея и , мы можем задать луч, а именно , который проходит из точки до бесконечно отдалённого источника освещения. Пересекает ли этот луч другой объект? Если нет, то между точкой и источником ничего нет, то есть мы можем вычислить освещённость от этого источника и прибавить его к общей освещённости. Если пересекает, то мы игнорируем этот источник.
Мы уже знаем, как вычислить ближайшее пересечение между лучом и сферой; мы используем его для трассировки лучей от камеры. Мы снова можем использовать его для вычисления ближайшего пересечения между лучом света и остальной сценой.
Однако параметры немного отличаются. Вместо того, чтобы начинаться с камеры, лучи испускаются из . Направление равно не , а . И нас интересуют пересечения со всем после на бесконечное расстояние; это значит, что и .
Мы можем обрабатывать точечные источники очень похожим образом, но с двумя исключениями. Во-первых, не задан , но его очень просто вычислить из позиции источника и . Во-вторых, нас интересуют любые пересечения, начиная с , но только до (в противном случае, объекты за источником освещения могли бы создавать тени!); то есть в этом случае и .
Существует один пограничный случай, который нам нужно рассмотреть. Возьмём луч . Если мы будем искать пересечения, начиная с , то мы, вероятнее всего, найдём саму при , потому что действительно находится на сфере, и ; другими словами, каждый объект будет отбрасывать тени на самого себя (Примечание: если точнее, то мы хотим избежать ситуации, при которой точка, а не весь объект, отбрасывает тень на саму себя; объект с более сложной чем сфера формой (а именно любой вогнутый объект) может отбрасывать истинные тени на самого себя!
Простейший способ справиться с этим - использовать в качестве нижней границы значений вместо малое значение . Геометрически, мы хотим сделать так, чтобы луч начинается немного вдали от поверхности, то есть рядом с , но не точно в . То есть для направленных источников интервал будет , а для точечных - .
В предыдущей версии TraceRay вычислял ближайшее пересечение луч-сфера, а затем вычислял освещение в пересечении. Нам нужно извлечь код ближайшего пересечения, поскольку мы хотим использовать его снова для вычисления теней:
ClosestIntersection(O, D, t_min, t_max) {
closest_t = inf
closest_sphere = NULL
for sphere in scene.Spheres {
t1, t2 = IntersectRaySphere(O, D, sphere)
if t1 in and t1 < closest_t
closest_t = t1
closest_sphere = sphere
if t2 in and t2 < closest_t
closest_t = t2
closest_sphere = sphere
}
return closest_sphere, closest_t
}
В результате TraceRay получается гораздо проще:
TraceRay(O, D, t_min, t_max) {
closest_sphere, closest_t = ClosestIntersection(O, D, t_min, t_max)
if closest_sphere == NULL
return BACKGROUND_COLOR
P = O + closest_t*D # Compute intersection
N = P - closest_sphere.center # Compute sphere normal at intersection
N = N / length(N)
return closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular)
}
Теперь нам нужно добавить в ComputeLighting проверку тени:
ComputeLighting(P, N, V, s) {
i = 0.0
for light in scene.Lights {
if light.type == ambient {
i += light.intensity
} else {
if light.type == point {
L = light.position - P
t_max = 1
} else {
L = light.direction
t_max = inf
}
# Проверка тени
shadow_sphere, shadow_t = ClosestIntersection(P, L, 0.001, t_max)
if shadow_sphere != NULL
continue
# Диффузность
n_dot_l = dot(N, L)
if n_dot_l > 0
i += light.intensity*n_dot_l/(length(N)*length(L))
# Зеркальность
if s != -1 {
R = 2*N*dot(N, L) - L
r_dot_v = dot(R, V)
if r_dot_v > 0
i += light.intensity*pow(r_dot_v/(length(R)*length(V)), s)
}
}
}
return i
}
Вот как будет выглядеть наша заново отрендеренная сцена:
Исходный код и рабочее демо >>
Теперь у нас уже что-то получается.
Давайте посмотрим, как работают зеркала. Когда мы смотрим в зеркало, то видим лучи света, отражающиеся от зеркала. Лучи света отражаются симметрично относительно нормали поверхности:
Допустим, мы трассируем луч и ближайшим пересечением оказывается зеркало. Какой цвет имеет луч света? Очевидно, то не цвет зеркала, а любой цвет, который имеет отражённый луч. Всё, что нам нужно - вычислить направление отражённого луча и выяснить, каким был цвет света, падающего из этого направления. Вот бы у нас была функция, возвращающая для заданного луча цвет света, падающего из этого направления…
О, постойте, у нас же она есть: она называется TraceRay .
Итак, мы начинаем с основного цикла TraceRay , чтобы увидеть, что «видит» луч, испущенный из камеры. Если TraceRay определяет, что луч видит отражающий объект, то он просто должен вычислить направление отражённого луча и вызвать… сам себя.
На этом этапе, я предлагаю вам перечитать последние три параграфа, пока вы их не поймёте. Если вы впервые читаете о рекурсивной трассировке лучей, то возможно вам понадобится перечитать пару раз, и немного подумать, прежде чем вы действительно поймёте .
Не торопитесь, я подожду.
Теперь, когда эйфория от этого прекрасного момента эврика! немного спала, давайте немного это формализируем.
Самое важное во всех рекурсивных алгоритмах - предотвратить бесконечный цикл. В этом алгоритме есть очевидное условие выхода: когда луч или падает на неотражающий объект, или когда он ни на что не падает. Но есть простой случай, в котором мы можем угодить в бесконечный цикл: эффект бесконечного коридора . Он проявляется, когда вы ставите зеркало напротив другого зеркала и видите в них бесконечные копии самого себя!
Есть множество способов предотвращения этой проблемы. Мы введём предел рекурсии алгоритма; он будет контролировать «глубину», на которую он сможет уйти. Давайте назовём его . При , то видим объекты, но без отражений. При мы видим некоторые объекты и отражения некоторых объектов. При мы видим некоторые объекты, отражения некоторых объектов и отражения некоторых отражений некоторых объектов . И так далее. В общем случае, нет особого смысла уходить вглубь больше чем на 2-3 уровня, потому что на этом этапе разница уже едва заметна.
Мы создадим ещё одно разграничение. «Отражаемость» не должна иметь значение «есть или нет» - объекты могут быть частично отражающими и частично цветными. Мы назначим каждой поверхности число от до , определяющее её отражаемость. После чего мы будем смешивать локально освещённый цвет и отражённый цвет пропорционально этому числу.
И наконец, нужно решить, какие параметры должен получать рекурсивный вызов TraceRay ? Луч начинается с поверхности объекта, точки . Направление луча - это направление света, отразившегося от ; в TraceRay у нас есть , то есть направление от камеры к , противоположное движению света, то есть направление отражённого луча будет , отражённый относительно . Аналогично тому, что происходит с тенями, мы не хотим, чтобы объекты отражали сами себя, поэтому . Мы хотим видеть объекты отражёнными вне зависимости от того, насколько они отдалены, поэтому . И последнее - предел рекурсии на единицу меньше, чем предел рекурсии, в котором мы находимся в текущий момент.
Как и ранее, в первую очередь мы изменяем сцену:
Sphere {
center = (0, -1, 3)
radius = 1
color = (255, 0, 0) # Красный
specular = 500 # Блестящий
reflective = 0.2 # Немного отражающий
}
sphere {
center = (-2, 1, 3)
radius = 1
color = (0, 0, 255) # Синий
specular = 500 # Блестящий
reflective = 0.3 # Немного более отражающий
}
sphere {
center = (2, 1, 3)
radius = 1
color = (0, 255, 0) # Зелёный
specular = 10 # Немного блестящий
reflective = 0.4 # Ещё более отражающий
}
sphere {
color = (255, 255, 0) # Жёлтый
center = (0, -5001, 0)
radius = 5000
specular = 1000 # Очень блестящий
reflective = 0.5 # Наполовину отражающий
}
Мы используем формулу «луча отражения» в паре мест, поэтому может избавиться от неё. Она получает луч и нормаль , возвращая , отражённый относительно :
ReflectRay(R, N) {
return 2*N*dot(N, R) - R;
}
Единственным изменением в ComputeLighting является замена уравнения отражения на вызов этого нового ReflectRay .
В основной метод внесено небольшое изменение - нам нужно передать TraceRay верхнего уровня предел рекурсии:
Color = TraceRay(O, D, 1, inf, recursion_depth)
Константе recursion_depth можно задать разумное значение, например, 3 или 5.
Единственные важные изменения происходят ближе к концу TraceRay , где мы рекурсивно вычисляем отражения:
TraceRay(O, D, t_min, t_max, depth) {
closest_sphere, closest_t = ClosestIntersection(O, D, t_min, t_max)
if closest_sphere == NULL
return BACKGROUND_COLOR
# Вычисление локального цвета
P = O + closest_t*D # Вычисление точки пересечения
N = P - closest_sphere.center # Вычисление нормали к сфере в точке пересечения
N = N / length(N)
local_color = closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular)
# Если мы достигли предела рекурсии или объект не отражающий, то мы закончили
r = closest_sphere.reflective
if depth <= 0 or r <= 0:
return local_color
# Вычисление отражённого цвета
R = ReflectRay(-D, N)
reflected_color = TraceRay(P, R, 0.001, inf, depth - 1)
return local_color*(1 - r) + reflected_color*r
}
Пусть результаты говорят сами за себя:
Чтобы лучше понять предел глубины рекурсии, давайте ближе рассмотрим рендер с :
А вот тот же увеличенный вид той же сцены, на этот раз отрендеренный с :
Как вы видите, разница заключается в том, видим ли мы отражения отражений отражений объектов, или только отражения объектов.
Давайте начнём с положения. Вы наверно заметили, что используется во всём псевдокоде только один раз: в качестве начальной точки лучей, исходящих из камеры в методе верхнего уровня. Если мы хотим поменять положение камеры. то единственное , что нужно сделать - это использовать другое значение для .
Влияет ли изменение положения на направление лучей? Ни в коей мере. Направление лучей - это вектор, проходящий из камеры на плоскость проекции. Когда мы перемещаем камеру, плоскость проекции двигается вместе с камерой, то есть их относительные положения не изменяются.
Давайте теперь обратим внимание на направление. Допустим, у нас есть матрица поворота, которая поворачивает в нужном направлении обзора, а - в нужное направление «вверх» (и поскольку это матрица поворота, то по определению она должна делать требуемое для ). Положение камеры не меняется, если вы просто вращаете камеру вокруг. Но направление меняется, оно просто подвергается тому же повороту, что и вся камера. То есть если у нас есть направление и матрица поворота , то повёрнутый - это просто .
Меняется только функция верхнего уровня:
For x in [-Cw/2, Cw/2] {
for y in [-Ch/2, Ch/2] {
D = camera.rotation * CanvasToViewport(x, y)
color = TraceRay(camera.position, D, 1, inf)
canvas.PutPixel(x, y, color)
}
}
Вот как выглядит наша сцена при наблюдении из другого положения и при другой ориентации:
На самом деле, трассировщики лучей относятся к классу алгоритмов, называемому чрезвычайно параллелизуемым именно потому что, сама их природа позволяет очень просто их распараллеливать.
K1 = dot(D, D)
k2 = 2*dot(OC, D)
k3 = dot(OC, OC) - r*r
Некоторые из этих значений постоянны для всей сцены - как только вы узнаете, как расположены сферы, r*r и dot(OC, OC) больше не меняются. Можно вычислить их один раз во время загрузки сцены и хранить их в самих сферах; вам просто нужно будет пересчитать их, если сферы должны переместиться в следующем кадре. dot(D, D) - это константа для заданного луча, поэтому можно вычислить его в ClosestIntersection и передать в IntersectRaySphere .
То есть когда мы ищем объекты между точкой и источником освещения, можно сначала проверить, не накладывает ли на текущую точку тень последний объект, накладывавший тень на предыдущую точку относительно того же источника освещения. Если это так, то мы можем закончить; если нет, то просто продолжаем обычным способом проверять остальные объекты.
Аналогично, при вычислении пересечения между лучом света и объектами в сцене на самом деле нам не нужно ближайшее пересечение - достаточно знать, что существует по крайней мере одно пересечение. Можно использовать специальную версию ClosestIntersection , которая возвращает результат, как только найдёт первое пересечение (и для этого нам нужно вычислять и возвращать не closest_t , а просто булево значение).
Подробное рассмотрение таких структур не относится к тематике нашей статьи, но общая идея такова: предположим, что у нас есть несколько близких друг к другу сфер. Можно вычислить центр и радиус наименьшей сферы, содержащей все эти сферы. Если луч не пересекает эту граничную сферу, то можно быть уверенным, что он не пересекает ни одну содержащуюся в нём сферу, и сделать это можно за одну проверку пересечения. Разумеется, если он пересекает сферу, то нам всё равно нужно проверять, пересекает ли он какую-нибудь из содержащихся в ней сфер.
Подробнее об этом можно узнать, прочитав о иерархии ограничивающих объёмов .
Предположим, мы трассируем лучи для пикселей и , и они падают на один объект. Можно логически предположить, что луч для пикселя тоже будет падать на тот же объект, пропустить начальный поиск пересечений со всей сценой и перейти непосредственно к вычислению цвета в этой точке.
Если сделать так в горизонтальном и вертикальном направлениях, то можно выполнять максимум на 75% меньшей первичных вычислений пересечений луч-сцена.
Разумеется, так можно запросто пропустить очень тонкий объект: в отличие от рассмотренных ранее, это «неправильная» оптимизация, потому что результаты её использования не идентичны тому, что бы мы получили без неё; в каком-то смысле, мы «жульничаем» на этой экономии. Хитрость в том, как догадаться сэкономить правильно, обеспечив удовлетворительные результаты.
Заметьте, что с точки зрения TraceRay может подойти любой объект, пока для него нужно вычислять только два значения: значение для ближайшего пересечения между лучом и объектом, и нормаль в точке пересечения. Всё остальное в трассировщике лучей не зависит от типа объекта.
Хорошим выбором будут треугольники. Сначала нужно вычислить пересечение между лучом и плоскостью, содержащей треугольник, и если пересечение есть, то определить, находится ли точка внутри треугольника.
Как это работает? Для каждого объекта можно вычислить места, где луч входит и выходит из объекта; например, в случае сферы луч входит в и выходит в . Предположим, что нам нужно вычислить пересечение двух сфер; луч находится внутри пересечения, когда находится внутри обеих сфер, и снаружи в противоположном случае. В случае вычитания луч находится внутри, когда он находится внутри первого объекта, но не внутри второго.
В более общем виде, если мы хотим вычислить пересечение между лучом и (где - любой булевый оператор), то сначала нужно по отдельности вычислить пересечение луч- и луч- , что даёт нам «внутренний» интервал каждого объекта и . Затем мы вычисляем , который находится во «внутреннем» интервале . Нам нужно просто найти первое значение , которое находится и во «внутреннем» интервале и в интервале , которые нас интересуют:
Нормаль в точке пересечения является или нормалью объекта, создающего пересечение, или её противоположностью, в зависимости от того, глядим ли мы «снаружи» или «изнутри» исходного объекта.
Разумеется, и не обязаны быть примитивами; они сами могут быть результатами булевых операций! Если реализовать это чисто, то нам даже не потребуется знать, чем они являются, пока мы можем получить из них пересечения и нормали. Таким образом, можно взять три сферы и вычислить, например, .
Реализация прозрачности очень похожа на реализацию отражения. Когда луч падает на частично прозрачную поверхность, мы, как и ранее, вычисляем локальный и отражённый цвет, но ещё и вычисляем дополнительный цвет - цвет света, проходящего сквозь объект, полученный ещё одним вызовом TraceRay . Затем нужно смешать этот цвет с локальным и отражённым цветами с учётом прозрачности объекта, и на этом всё.
Например, приблизительно равен , а приблизительно равен . То есть для луча, входящего в воду под углом получаем
Остановитесь на мгновение и осознайте: если реализовать конструктивную блочную геометрию и прозрачность, то можно смоделировать увеличительное стекло (пересечение двух сфер), которое будет вести себя как физически правильное увеличительное стекло!
Однако не забывайте об аналогии, с которой мы начинали: каждый луч должен задавать «определяющий» цвет каждого квадрата «сетки», через которую мы смотрим. Используя по одному лучу на писель, мы условно решаем, что цвет луча света, проходящего через середину квадрата, определяет весь квадрат, но это может быть и не так.
Решить эту проблему можно трассированием нескольких лучей на пиксель - 4, 9, 16, и так далее, а затем усредняя их, чтобы получить цвет пикселя.
Разумеется, при этом трассировщик лучей становится в 4, 9 или 16 раз медленнее, по той же причине, по которой субдискретизация делает его в раз быстрее. К счастью, существует компромисс. Мы можем предположить, что свойства объекта вдоль его поверхности меняются плавно, то есть испускание 4 лучей на пиксель, которые падают на один объект в немного отличающихся точках, не слишком улучшит вид сцены. Поэтому мы можем начать с одного луча на пиксель и сравнивать соседние лучи: если они падают на другие объекты или их цвет отличается больше, чем на переделённое пороговое значение, то применяем к обоим подразделение пикселей.
CanvasToViewport(x, y) {
return (x*Vw/Cw, y*Vh/Ch, d)
}
ReflectRay(R, N) {
return 2*N*dot(N, R) - R;
}
ComputeLighting(P, N, V, s) {
i = 0.0
for light in scene.Lights {
if light.type == ambient {
i += light.intensity
} else {
if light.type == point {
L = light.position - P
t_max = 1
} else {
L = light.direction
t_max = inf
}
# Проверка теней
shadow_sphere, shadow_t = ClosestIntersection(P, L, 0.001, t_max)
if shadow_sphere != NULL
continue
# Диффузность
n_dot_l = dot(N, L)
if n_dot_l > 0
i += light.intensity*n_dot_l/(length(N)*length(L))
# Блеск
if s != -1 {
R = ReflectRay(L, N)
r_dot_v = dot(R, V)
if r_dot_v > 0
i += light.intensity*pow(r_dot_v/(length(R)*length(V)), s)
}
}
}
return i
}
ClosestIntersection(O, D, t_min, t_max) {
closest_t = inf
closest_sphere = NULL
for sphere in scene.Spheres {
t1, t2 = IntersectRaySphere(O, D, sphere)
if t1 in and t1 < closest_t
closest_t = t1
closest_sphere = sphere
if t2 in and t2 < closest_t
closest_t = t2
closest_sphere = sphere
}
return closest_sphere, closest_t
}
TraceRay(O, D, t_min, t_max, depth) {
closest_sphere, closest_t = ClosestIntersection(O, D, t_min, t_max)
if closest_sphere == NULL
return BACKGROUND_COLOR
# Вычисление локального цвета
P = O + closest_t*D # Вычисление точки пересечения
N = P - closest_sphere.center # Вычисление нормали сферы в точке пересечения
N = N / length(N)
local_color = closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular)
# Если мы достигли предела рекурсии или объект не отражающий, то мы закончили
r = closest_sphere.reflective
if depth <= 0 or r <= 0:
return local_color
# Вычисление отражённого цвета
R = ReflectRay(-D, N)
reflected_color = TraceRay(P, R, 0.001, inf, depth - 1)
return local_color*(1 - r) + reflected_color*r
}
for x in [-Cw/2, Cw/2] {
for y in [-Ch/2, Ch/2] {
D = camera.rotation * CanvasToViewport(x, y)
color = TraceRay(camera.position, D, 1, inf)
canvas.PutPixel(x, y, color)
}
}
А вот сцена, использованная для рендеринга примеров:
Viewport_size = 1 x 1 projection_plane_d = 1 sphere { center = (0, -1, 3) radius = 1 color = (255, 0, 0) # Красный specular = 500 # Блестящий reflective = 0.2 # Немного отражающий } sphere { center = (-2, 1, 3) radius = 1 color = (0, 0, 255) # Синий specular = 500 # Блестящий reflective = 0.3 # Немного более отражающий } sphere { center = (2, 1, 3) radius = 1 color = (0, 255, 0) # Зелёный specular = 10 # Немного блестящий reflective = 0.4 # Ещё более отражающий } sphere { color = (255, 255, 0) # Жёлтый center = (0, -5001, 0) radius = 5000 specular = 1000 # Очень блестящий reflective = 0.5 # Наполовину отражающий } light { type = ambient intensity = 0.2 } light { type = point intensity = 0.6 position = (2, 1, 0) } light { type = directional intensity = 0.2 direction = (1, 4, 4) }
Теги: Добавить метки
ВВЕДЕНИЕ
Существует несколько методов генерации реалистичных изображений, таких как прямая трассировка лучей (трассировка фотонов) и обратная трассировка лучей.
Методы трассировки лучей на сегодняшний день считаются наиболее мощными и универсальными методами создания реалистичных изображений. Известно много примеров реализации алгоритмов трассировки для качественного отображения самых сложных трехмерных сцен. Можно отметить, что универсальность методов трассировки в значительной степени обусловлена тем, что в их основе лежат простые и ясные понятия, отражающие наш опыт восприятия окружающего мира.
Окружающие нас объекты обладают по отношению к свету такими свойствами:
излучают;
отражают и поглощают;
пропускают сквозь себя.
Каждое из этих свойств можно описать некоторым набором характеристик.
Излучение можно охарактеризовать интенсивностью и спектром.
Свойство отражения (поглощения) можно описать характеристиками диффузного рассеивания и зеркального отражения. Прозрачность можно описать ослаблением интенсивности и преломлением.
Из точек поверхности (объема) излучающих объектов исходят лучи света. Можно назвать такие лучи первичными - они освещают все остальное. От источников излучения исходит по различным направлениям бесчисленное множество первичных лучей. Некоторые лучи уходят в свободное пространство, а некоторые попадают на другие объекты.
В результате действия на объекты первичных лучей возникают вторичные лучи. Некоторые из них попадают на другие объекты. Так, многократно отражаясь и преломляясь, отдельные световые лучи приходят в точку наблюдения. Таким образом, изображение сцены формируется некоторым множеством световых лучей.
Цвет отдельных точек изображения определяется спектром и интенсивностью первичных лучей источников излучения, а также поглощением световой энергии в объектах, встретившихся на пути соответствующих лучей.
Непосредственная реализация данной лучевой модели формирования изображения представляется затруднительной. Можно попробовать построить алгоритм построения изображения указанным способом. В таком алгоритме необходимо предусмотреть перебор всех первичных лучей и определить те из них, которые попадают в объекты и в камеру. Затем выполнить перебор всех вторичных лучей, и также учесть только те, которые попадают в объекты и в камеру. И так далее. Такой алгоритм называется прямой трассировкой лучей. Главный недостаток этого метода - много лишних операций, связанных с расчетом лучей, которые затем не используются.
1. ОБРАТНАЯ ТРАССИРОВКА ЛУЧЕЙ
Именно этому методу генерации реалистичных изображений посвящена эта работа.
Метод обратной трассировки лучей позволяет значительно сократить перебор световых лучей. Метод разработан в 80-х годах, основополагающими считаются работы Уиттеда и Кэя. Согласно этому методу отслеживание лучей производится не от источников света, а в обратном направлении - от точки наблюдения. Так учитываются только те лучи, которые вносят вклад в формирование изображения.
Плоскость проецирования разбита на множество пикселов. Выберем центральную проекцию с центром схода на некотором расстоянии от плоскости проецирования. Проведем прямую линию из центра схода через середину пиксела плоскости проецирования. Это будет первичный луч обратной трассировки. Если этот луч попадет в один или несколько объектов сцены, то выбираем ближайшую точку пересечения. Для определения цвета пиксела изображения нужно учитывать свойства объекта, а также то, какое световое излучение приходится на соответствующую точку объекта.
Если объект зеркальный (хотя бы частично), то строим вторичный луч - луч падения, считая лучом отражения предыдущий, первичный трассируемый луч.
Для идеального зеркала достаточно затем проследить лишь очередную точку пересечения вторичного луча с некоторым объектом. У идеального зеркала идеально ровная отполированная поверхность, поэтому одному отраженному лучу соответствует только один падающий луч. Зеркало может быть затемненным, то есть поглощать часть световой энергии, но все равно остается правило: один луч падает - один отражается.
Если объект прозрачный, то необходимо построить новый луч, такой, который при преломлении давал бы предыдущий трассируемый луч.
Для диффузного отражения интенсивность отраженного света, как известно, пропорциональна косинусу угла между вектором луча от источника света и нормалью.
Когда выясняется, что текущий луч обратной трассировки не пересекает какой-либо объект, а уходит в свободное пространство, то на этом трассировка для этого луча заканчивается.
При практической реализации метода обратной трассировки вводят ограничения. Некоторые из них необходимы, чтобы можно было в принципе решить задачу синтеза изображения, а некоторые ограничения позволяют значительно повысить быстродействие трассировки.
Ограничения при реализации трассировки
Среди всех типов объектов выделим некоторые, которые назовем источниками света. Источники света могут только излучать свет, но не могут его отражать или преломлять. Будем рассматривать только точечные источники света.
Свойства отражающих поверхностей описываются суммой двух составляющих - диффузной и зеркальной.
В свою очередь, зеркальность также описывается двумя составляющими. Первая (reflection) учитывает отражение от других объектов, не являющихся источниками света. Строится только один зеркально отраженный луч r для дальнейшей трассировки. Вторая компонента (specular) означает световые блики от источников света. Для этого направляются лучи на все источники света и определяются углы, образуемые этими лучами с зеркально отраженным лучом обратной трассировки (r). При зеркальном отражении цвет точки поверхности определяется собственным цветом того, что отражается.
При диффузном отражении учитываются только лучи от источников света. Лучи от зеркально отражающих поверхностей ИГНОРИРУЮТСЯ. Если луч, направленный на данный источник света, закрывается другим объектом, значит, данная точка объекта находится в тени. При диффузном отражении цвет освещенной точки поверхности определяется собственным цветом поверхности и цветом источников света.
Для прозрачных (transparent) объектов не учитывается зависимость коэффициента преломления от длины волны. (Иногда прозрачность вообще моделируют без преломления, то есть направление преломленного луча t совпадает с направлением падающего луча.)
Для учета освещенности объектов светом, рассеянным другими объектами, вводится фоновая составляющая (ambient).
Для завершения трассировки вводится ограничение количества итераций (глубины рекурсии).
Выводы по методу обратной трассировки
Достоинства:
Универсальность метода, его применимость для синтеза изображений достаточно сложных пространственных схем. Воплощает многие законы геометрической оптики. Просто реализуются разнообразные проекции.
Даже усеченные варианты данного метода позволяют получить достаточно реалистичные изображения. Например, если ограничиться только первичными лучами (из точки проецирования), то это дает удаление невидимых точек. Трассировка уже одного-двух вторичных лучей дает тени, зеркальность прозрачность.
Все преобразования координат линейны, поэтому достаточно просто работать с текстурами.
Недостатки:
Проблемы с моделированием диффузного отражения и преломления.
Для каждой точки изображения необходимо выполнять много вычислительных операций. Трассировка относится к числу самых медленных алгоритмов синтеза изображений.
2. КОНСТРУКТОРСКАЯ ЧАСТЬ
Алгоритмы.
Обратная трассировка лучей.
Рис. 1 - Блок-схема рекуррентного алгоритма обратной трассировки лучей
трассировка луч программирование язык
В этой программе алгоритм обратной трассировки
реализован рекуррентным образом. Функция расчета интенсивности первичного луча
рекуррентно вызывает саму себя для нахождения интенсивностей отраженного и
преломленного лучей.
Алгоритм:
Для расчета цвета каждого пиксела буфера кадра выполняются следующие действия:
Найти координаты пиксела в мировой системе координат.
Найти координаты первичного луча.
Запуск функции вычисления интенсивности первичного луча.
Найти пересечения луча со всеми примитивами сцены и выбрать ближайшее.
Если пересечение не найдено, значит, луч ушел в свободное пространство.
Для расчета цвета принимаем полную интенсивность равной фоновой интенсивности. Перейти на шаг 12. Если пересечение найдено, перейти на шаг 6.
Рассчитываем «локальную» интенсивность цвета объекта, которому принадлежит точка пересечения. Под «локальной» интенсивностью понимается интенсивность с учетом интенсивности диффузно отраженного света и интенсивность бликов.
Если материал отражает свет только диффузно, то считаем интенсивности отраженного и преломленного света нулевыми. Перейти на шаг 12. Иначе перейти на шаг 8.
Если достигнута максимальная глубина рекурсии, то принять интенсивности отраженного и преломленного света нулевыми. Перейти на шаг 12. Иначе перейти на шаг 9.
Вычислить вектор отраженного луча. Запуск рекурсии для нахождения интенсивности отраженного луча.
Вычислить вектор преломленного луча. Запуск рекурсии для нахождения интенсивности преломленного луча.
Вычисление полной интенсивности цвета. Полная интенсивность включает в себя интенсивность рассеянного света, локальную интенсивность и интенсивности отраженного и преломленного лучей.
Возврат в точку вызова функции вычисления интенсивности луча.
Если шел расчет первичного луча, то в буфер кадра помещается пиксел вычисленного цвета. Переходим к расчету следующего пиксела буфера кадра Если шел расчет отраженного (преломленного) луча, то вычисленная интенсивность будет принята как интенсивность отраженного (преломленного) луча на предыдущем шаге рекурсии.
Построение теней.
Сплошные тени.
Для построения сплошных теней в алгоритме трассировки на этапе вычисления «локальной» интенсивности цвета в точке объекта проверяется «видимость» каждого источника света из этой точки.
Принцип работы алгоритма.
Из проверяемой точки строится луч, направленный на источник света.
Производится поиск пересечений этого луча с примитивами сцены между проверяемой точкой и источником.
Если найдено хотя бы одно пересечение, то проверяемая точка находится в тени. При расчете ее цвета источник, для которого проводилась проверка, не учитывается.
проверяемый источник.
Такой метод нахождения теней дает приемлемый
результат до тех пор, пока на сцене нет прозрачных объектов. Однако сплошная
черная тень от стакана выглядит не реалистично. Стекло частично пропускает
свет, поэтому интенсивность заслоненного источника должна учитываться при
подсчете интенсивности света в точке объекта, но она должна ослабляться при
проходе света сквозь стекло.
Математические и физические предпосылки алгоритма обратной трассировки лучей.
Освещение.
Интенсивность света складывается из интенсивности фоновой подсветки сцены, интенсивности диффузно отраженного света источников, интенсивности бликов от источников («локальные» характеристики освещенности), интенсивности зеркально отраженного луча и интенсивности преломленного луча, если таковые имеются.
Интенсивность фоновой подсветки (IA) задается некоторой константой.
Интенсивность диффузно отраженного света (ID)
вычисляется по классическому «закону косинуса».
ID = IL cos α,(2.2.1.6)
где IL - интенсивность источника света, α - угол между нормалью к поверхности и направлением на источник.
В случае освещения сцены несколькими источниками
Id вычисляется для каждого из них и затем суммируются.
IDi =Σ
ILi cos αi.(2.2.1.7)
Интенсивность блика от источника (IS)
вычисляется в соответствии с моделью Фонга.
IS = IL cosp β,(2.2.1.8)
где IL - интенсивность источника света (0<=IL<=1), β - угол между отраженным лучом от источника света и направлением на точку, в которой расположена камера (центр проекции), p - некоторая степень от 1 до 200 -влияет на размытость блика. При
маленьких значениях p блик более размытый.
Как и при вычислении ID в случае освещения сцены
несколькими источниками IS вычисляется отдельно для каждого источника, а затем
результаты суммируются.
ISi =Σ
ILi cosp β i.(2.2.1.9)
Интенсивности зеркально отраженного (IR) и преломленного (IT) света рассчитываются для отраженного и преломленного лучей на следующем шаге рекурсии. Если достигнут предел глубины рекурсии, то эти интенсивности берутся нулевыми. От интенсивности IR берется r процентов, а от IT - t = 1 - r (см. предыдущий раздел).
Кроме того, вводятся следующие коэффициенты: KD - коэффициент диффузного отражения поверхности, KS - коэффициент блика.- этот коэффициент является характеристикой неровности отражающей поверхности. Чем больше неровность поверхности, тем меньше света отражается от неё зеркально и меньше света она пропускает, и соответственно больше света она отражает диффузно. 0 <= KD <= 1.
При KD = 0 - весь свет, падающий на поверхность, отражается и преломляется. KD = 1 - весь свет отражается диффузно. На этот коэффициент умножаются интенсивность диффузно отраженного света и интенсивность фоновой подсветки. Интенсивности зеркально отраженного и преломленного света умножаются на (1 - KD).- этот коэффициент отвечает за яркость блика от источника. 0<=KS<=1.
При KS = 0 - блик не виден, при KS = 1 - яркость блика максимальна.
Таким образом, окончательная формула для расчета
интенсивности объекта в какой-либо точке будет выглядеть следующим образом:
I = IAKD + Σ(ILiKDcos
αi
+ ILiKScosp β i) + (1 - KD)(IRr
+ IT(1 - r)).(2.2.1.10)
При этом надо заметить, что итоговая интенсивность не должна получиться больше единицы. Если такое происходит, то эта точка изображения будет засвеченной. Ее интенсивность надо сбросить на единицу.
Для получения цветного изображения необходимо провести расчеты отдельно для красной, зеленой и синей компоненты света. Цвет пиксела изображения будет вычисляться путем умножения каждой компоненты интенсивности на число, определяющее максимальное количество градаций интенсивности изображения. Для 32-битного изображения оно равно 255 на каждый из цветов(R,G,B).
255*IR,= 255*IG,= 255*IB.
Здесь IR (не путать с интенсивностью зеркально отраженного света), IG, IB - интенсивности трех компонент света в точке, полученная по формуле, указанной выше.
Коэффициенты KD, KS, p - это индивидуальные характеристики объекта, отражающие его свойства. Кроме этого имеется еще один коэффициент - абсолютный показатель преломления n. n = c / v, где c - скорость света в вакууме, v - скорость света в среде (внутри объекта). Для абсолютно непрозрачных тел этот коэффициент равен ∞ (т.к. скорость света внутри тела нулевая). В программе для задания абсолютно непрозрачного тела необходимо поставить этот коэффициент >> 1 (порядка 10 000). При этом доля зеркально отраженного света r будет стремиться к единице, а преломленного, соответственно, к нулю.
Вычисление нормалей.
В алгоритме трассировки нормали к объектам необходимы для вычисления отраженного и преломленного лучей, а также для определения освещенности согласно модели Фонга.
В этой программе присутствуют три вида примитивов, из которых строится сцена. Это полигон (треугольник), эллипсоид и параболоид. Последние два введены для более реалистичной имитации стакана (его можно было бы построить и из полигонов, но модель получилась бы более грубая).
Вычисление нормали к полигону (треугольнику).
Вычисление нормали к треугольнику сводится к
операции векторного умножения. Пусть задан треугольник ABC координатами трех
своих вершин:
XA, YA, ZA, XB, YB, ZB, XC, YC, ZC.
Вычислим координаты двух векторов, например AB и AC:
XB - XA,= XB - XA,
ZAB = XB - XA,(2.2.2.1)= XC - XA,=
XC - XA,= XC - XA.
Координаты вектора нормали будут вычисляться по формулам:
YABZAC - YACZAB,= XABZAC - XACZAB,(2.2.2.2)=
XABYAC - XACYAB.
Нет необходимости вычислять координаты вектора нормали к треугольнику каждый раз в теле трассировки, так как в любой точке треугольника нормали одинаковые. Достаточно их посчитать один раз в инициализирующей части программы и сохранить. При повороте треугольника надо поворачивать и его нормаль.
Вычисление нормали к поверхности второго порядка.
Поверхность второго порядка задается в общем
случае уравнением вида:
Q(x,y,z) = a1x2 + a2y2 + a3z2 + b1yz
+ b2xz + b3xy + c1x +c2y +c3z + d =0.
Но мы будем использовать другую форму записи.
Так уравнение эллипсоида будет выглядеть следующим образом:
(x-x0)2/A2 + (y-y0)2/B2 + (z-z0)2 /C2 =
1,(2.2.2.3)
где x0, y0, z0 - координаты центра эллипсоида, A, B, C - длины полуосей эллипсоида.
Уравнение параболоида:
(x-x0)2/A2 + (y-y0)2/B2 - (z-z0)2 /C2 =
1,(2.2.2.4)
где x0, y0, z0 - координаты центра параболоида, A, B, C - длины полуосей параболоида. Ось параболоида расположена вдоль оси Oz мировой системы координат. Для вычисления координат вектора нормали необходимо вычислить частные производные по x, y, z.
Координаты вектора нормали эллипсоида:
Yn = 2(y-y0)/B2,= 2(z-z0)/С2.
Направление вектора не изменится, если все его
координаты разделить на 2:
Xn = (x-x0)/A2,= (y-y0)/B2,(2.2.2.5)
Zn = (z-z0)/С2.
Координаты вектора нормали параболоида
вычисляются аналогично:
Xn = (x-x0)/A2,= (y-y0)/B2,(2.2.2.6)
Zn = - (z-z0)/С2.
Нормаль для поверхности второго порядка придется вычислять непосредственно в теле трассировки, так как в разных точках фигуры нормали разные.
Вычисление отраженного луча.
Пусть задан вектор падающего луча S, а также известен вектор нормали N. Требуется найти вектор отраженного луча R.
Рассмотрим единичные векторы R1, S1и N1.
Поскольку векторы нормали, падающего луча и отраженного луча находятся в одной
плоскости, то можно записать R1 + S1 = N`, где N` - это вектор, соответствующий
диагонали ромба и совпадающий по направлению с нормалью. Длина вектора N` равна
2cosθ.
Так
как вектор N` по направлению совпадает с N1, то
N` = N`2cosθ.
Отсюда найдем единичный вектор отраженного луча:
R1 = N1 2cosθ
- S1 = N/|N| 2cosθ
- S/|S|.
Найдем cosθ. Это можно сделать, используя скалярное произведение векторов N и S:
Полагая, что искомый вектор отраженного луча будет иметь такую же длину, что и вектор падающего луча, то есть R = |S| R1, получим
N 2NS/|N|2 - S.
Это решение в векторной форме. Запишем координаты вектора:
2xN(xNxS+yNyS+zNzS)/(xN2+yN2+zN2) - xS,=
2yN(xNxS+yNyS+zNzS)/(xN2+yN2+zN2) - yS,(2.2.3.1)=
2zN(xNxS+yNyS+zNzS)/(xN2+yN2+zN2) - zS.
Вычисление преломленного луча.
Пусть даны два единичных вектора: S1 - вектор падающего луча, и N1 - вектор нормали к границе раздела двух сред. Также должны быть известны два коэффициента преломления для данных сред - n1 и n2 (или их отношение).
Требуется найти единичный вектор преломленного луча T1. Для решения выполним некоторые геометрические построения.
Искомый вектор T1 равен сумме двух векторов:
Найдем вначале вектор NT. Он противоположен по
направлению вектору нормали, а его длина равна |T1| cos α2
= cos α2 (поскольку T1 - единичный). Таким
образом, NT = -N1 cos α2. Необходимо
определить cos α2. Запишем
закон преломления n1 sin α1 = n2 sin α2 в
виде:
sin α2 = n sin α1,
где n = n1 /
n2.
Воспользуемся тождеством cos2α
+ sin2α = 1. Тогда
cos α2 = √ 1 - sin2α2 = √ 1 - n2 sin2α1
cos α2 = √
(1 + n2 (cos2α1 - 1)
Значение cos α1 можно выразить через скалярное произведение единичных векторов S1 и N1, то есть cos α1 = S1N1. Тогда мы можем записать такое выражение для вектора NT:
N1√1+n2((S1N1)2 - 1).
Осталось найти выражение для вектора B. Он располагается на одной прямой с вектором A, причем A = S1 - NS. Учитывая, что NS равен N1 cos α1, то A = S1 - N1 cos α1. Так как cos α1 = S1N1, то A = S1 - N1 (S1N1).
Поскольку длина вектора A равна sin α1,
а
длина вектора B равна sin α2, то
|B|/|A| = sin α2/ sin α1 = n2/n1 = n,
откуда |B| = n |A|. Учитывая взаимное расположение векторов A и B, получим
NA =n(N1(S1N1) - S1).
Теперь мы можем записать искомое выражение для
единичного вектора луча преломления T1:
T1 = nN1 (S1N1) - nS1 - N1√1 +
n2 ((S1N1)2 - 1).(2.2.4.1)
Вычисление точки пересечения с примитивами.
В алгоритме трассировки для построения
изображения необходимо вычислять точки пересечения лучей с примитивами сцены.
Луч задается параметрическим уравнением прямой. Любая точка луча удовлетворяет
уравнению
R = A + Vt,(2.2.5.1)
где R - радиус вектор произвольной точки, принадлежащей лучу, A - радиус- вектор начальной точки луча, V - направляющий вектор луча, t - параметр.
Если направляющий вектор V нормализовать, то параметр t будет численно равен расстоянию от начальной точки луча A до точки R.
Можно записать это уравнение в координатном
виде:
x = x1 + at,= y1 + bt,(2.2.5.2)= z1
+ ct.
Здесь x1, y1, z1 - координаты начальной точки луча в прямоугольной декартовой мировой системе координат, a,b,c - координаты направляющего вектора луча.
Вычисление точки пересечения луча с поверхностью второго порядка.
Для нахождения точки пересечения луча, заданного
уравнениями (2) с поверхностью второго порядка, заданной уравнениями (2.2.2.3)
или (2.2.2.4):
(x-x0)2/A2 + (y-y0)2/B2 + (z-z0)2 /C2 = 1 (эллипсоид)
(x-x0)2/A2 + (y-y0)2/B2 - (z-z0)2 /C2 = 1
(параболоид),
нужно подставить в уравнение поверхности второго порядка вместо x, y и z соответствующие уравнения луча. В результате этого после раскрытия всех скобок и приведения подобных мы получим квадратное уравнение относительно параметра t. Если дискриминант квадратного уравнения меньше нуля, то луч и поверхность второго порядка общих точек пересечения не имеют. В противном случае можно будет вычислить два значения параметра t. Дискриминант может быть равен нулю - это соответствует предельному случаю касания луча поверхности, и мы получим два совпадающих значения параметра t.
Для нахождения координат точек пересечения луча и поверхности достаточно подставить найденные значения параметра t в уравнения луча (2).
В программе при нахождении двух пересечений для визуализации выбирается ближнее из них. Ближнее пересечение определяется путем сравнения найденных параметров t. Ближе к точке наблюдения находится то пересечение, которому соответствует меньший параметр t. Тут надо заметить, что в результате решения квадратного уравнения одно или оба значения параметра t могут получиться отрицательными. Это означает, что точка пересечения лежит «сзади» относительно точки начала луча, на половине прямой, находящейся «по нашу сторону» относительно картинной плоскости. Такие точки при поиске пересечения отбрасываются.
Кроме того, в программе для каждой фигуры введены верхняя и нижняя секущие плоскости. Отображается только часть фигуры, лежащая между ними.
Для этого после нахождения точки пересечения анализируется ее z-координата.
Вычисление точки пересечения луча с полигоном (треугольником).
Для вычисления точки пересечения луча, заданного уравнениями (2) необходимо сначала определить точку пересечения этого луча с плоскостью, содержащей этот треугольник.
Уравнение плоскости выглядит следующим образом:
Q(x, y, z) = Ax + By + Cz +D =
0.(2.2.5.3)
Здесь коэффициенты A, B, C совпадают с координатами нормали к этой плоскости. Координаты нормали плоскости совпадают с координатами нормали треугольника, которые мы посчитали на этапе загрузки сцены.
Для нахождения свободного члена D необходимо подставить координаты любой точки треугольника, например, одной из вершин.
Ax -By - Cz.(2.2.5.4)
По ходу выполнения программы значение D меняться не будет, поэтому его целесообразно посчитать при инициализации сцены и хранить, как и координаты нормали. Пересчитывать его необходимо только при изменении положения треугольника.
Теперь для нахождения точки пересечения подставим уравнения луча (2) в
уравнение плоскости.
(x1 + at) + B (y1 + bt) + C (z1 + ct) + D = 0
Откуда получим
= - (Ax1 + By1 + Cz1 + D) / (Aa + Bb + Cc)(2.2.5.5)
Если знаменатель этой дроби равен нулю, значит луч параллелен плоскости, в которой лежит треугольник. Точки пересечения нет.
Для нахождения координат точки пересечения надо подставить найденное значение параметра t в уравнения луча (2). Назовем точку пересечения D. Мы получим координаты xD, yD, zD.
Теперь необходимо определить, попала ли точка D
внутрь треугольника. Найдем координаты векторов AB, BC, CA (A, B, C - вершины
треугольника) и координаты векторов AD, BD, CD. Затем найдем три векторных
произведения:
nA = AB x AD,= BC x BD,(2.2.5.6)= CA x CD.
Эти вектора будут коллинеарны. Если все три вектора сонаправлены, то точка D лежит внутри треугольника. Сонаправленность определяется равенству знаков соответствующих координат всех трех векторов.
Операцию проверки принадлежности точки D треугольнику ABC можно ускорить. Если ортогонально спроецировать треугольник ABC и точку D на одну из плоскостей xOy, yOz или xOz, то попадание проекции точки в проекцию треугольника будет означить попадание самой точки в треугольник (конечно же, если уже известно, что точка D лежит в плоскости, содержащей треугольник ABC). При этом число операций заметно сокращается. Так для поиска координат всех векторов нужно искать по две координаты на каждый вектор, а при поиске векторных произведений нужно искать только одну координату (остальные равны нулю).
Для проверки сонаправленности векторов, полученных при вычислении векторного произведения нужно проверить знаки этой единственной координаты для всех трех векторов. Если все знаки больше нуля, или меньше нуля, то вектора сонаправлены. Равенство нулю одного из векторных произведений соответствует случаю, когда точка D попадает на прямую, содержащую одну из сторон треугольника.
Кроме того, перед вычислениями векторов и векторных произведений можно провести простой габаритный тест. Если проекция точки D лежит правее, левее, выше или ниже каждой из проекций вершин треугольника, то она не может лежать внутри.
Остается добавить, что для проецирования лучше выбирать ту из плоскостей, площадь проекции треугольника на которую больше. При таком условии исключается случай проецирования треугольника в отрезок (при условии, что проверяемый треугольник не вырожден в отрезок). Кроме того, при увеличении площади проекции уменьшается вероятность ошибки. Для определения такой плоскости проецирования достаточно проверить три координаты нормали треугольника. Если z-координата нормали больше (по абсолютному значению) x и y, то проецировать надо на плоскость xOy. Если y больше чем x и z, то проецируем на xOz. В оставшемся случае - на yOz.
Описание типов данных. Структура программы.
Описание модулей программы
Список модулей:.h-описание структуры TTex.h-описание структур TPlaneTex и TEllipsoidTex.h-описание структур TPoint2d и TPoint3d.h-описание страктуры TRGBColor.h-описание класса TLamp.h-описание класса TCam.h-описание класса TPrimitive.h-описание класса TFrstSurface.h-описание класса TScndSurface.h-описание класса TTriangle.h-описание класса TEllipsoid.h-описание класса TCylinder.h-описание класса THyperboloidVert.h-описание класса THyperboloidHor.h-описание класса TScene.h-описание класса TTracer
Модули реализующие, интерфейс программы:
Options.h-модуль формы «Опции»
ExtraCamOptions.h-модуль формы «Свойства камеры»
MainUnit.h-модуль главной формы программы
Краткое описание структур и классов программы:TPoint3d - структура, описывающая точку в мировой системе координат,TPoint2d - структура, описывающая точку на плоскости (в текстуре) с целочисленными координатами,TRGBColor - структура, описывающая цвет по трем составляющим (RGB),TTex - структура, описывающая текстуру - содержит адрес массива пикселей и его размеры,TPlaneTex - структура, описывающая привязку текстуры к плоскости.
Содержит три точки, к которым привязывается текстура:TLamp - класс, описывающий источник освещения.
Содержит объект TPoint3d coord с координатами источника и три переменные типа float (Ir, Ig, Ib) для хранения интенсивности трех компонент света.TCam - класс, описывающий камеру.
Содержит два угла (a, b), указывающих направление зрения камеры, точку, на которую направлена камера (viewP) и расстояние от камеры до этой точки (r). TPrimitive - абстрактный класс примитива. От него наследуются поверхности первого и второго порядка.TFrstSurface - абстрактный класс поверхности первого порядка. От него наследуется класс треугольника.TScndSurface - абстрактный класс поверхности второго порядка. От него наследуются классы эллипсоида и параболоида.TTriangle - класс треугольника. Содержит три вершины треугольника и его нормаль.TCylinder - класс цилиндра.THyperboloidVert - класс однополостного гиперболоида, лежащего вдоль оси oZ.THyperboloidHor -класс однополостного гиперболоида, лежащего вдоль оси oX.TEllipsoid - класс эллипсоида.TScene - класс сцены. Содержит информацию о всех примитивах, источниках и камере.TTracer - класс, отвечающий за построения изображения. Содержит буфер (buffer) разметом 400x400 пикселей, в котором формируется изображение сцены. Перед генерацией необходимо вызвать функциюпередав ей в качестве параметра указатель на сцену, которую необходимо сгенерировать. Для генерации вызвать функцию render.
Все классы - потомки TPrimitive предоставляют следующие функции:getT(TPoint3d p0, TPoint3d viewDir) - возвращает расстояние от точки начала(p0) луча viewDir до ближайшей точки пересечения с примитивом.
void getTArr(float* arr, int& n, TPoint3d p0, TPoint3d viewDir) - заполняет массив arr расстояниями от точки начала(p0) луча viewDir до ближайшей всех точек пересечения с примитивом.
void getNormal(TPoint3d& n, const TPoint3d& p) - возвращает координаты вектора нормали к примитиву в точке p.
void getColor(TRGBColor& c, const TPoint3d& p) - возвращает цвет примитива точке p (с учетом текстуры).
3. ТЕХНОЛОГИЧЕСКАЯ ЧАСТЬ
Выбор языка программирования.
При разработке программы был использован язык программирования высокого уровня C++ в составе среды визуального программирования CodeGear RAD Studio for Windows.
Данный язык был выбран благодаря тому, что он предоставляет максимально удобные средства по работе с оперативной памятью, позволяет реализовывать алгоритмы более эффективно, по сравнению с другими высокоуровневыми языками. Программы, написанные на C++, работают быстрее и занимают меньше места на диске.
Кроме того, среда визуального программирования CodeGear RAD Studio for Windows
предоставляет большое количество стандартных визуальных компонентов для создания интерфейса, и ряд библиотек с различными часто используемыми полезными функциями. Также у автора работы наибольший опыт программирования именно в указанной среде визуального программирования.
Форма «опции». Вкладка «освещение».
На этой вкладке находятся средства по настройке освещения сцены.
Координаты источника - координаты в мировой системе координат источника света, выбранного в выпадающем списке.
Интенсивность источника - значения трех компонент интенсивности источника света, выбранного в выпадающем списке.
Фоновая интенсивность - значения трех компонент фоновой интенсивности.
Кнопка “+” (рядом с выпадающим списком) - добавление нового источника света.
Кнопка “-” (рядом с выпадающим списком) - удаление источника света, выбранного в выпадающем списке.
Форма «опции». Вкладка «камера».
На этой вкладке находятся средства по настройке опций камеры.
Предосмотр - здесь можно увидеть примерный вид изображения до его генерации.
Навигация - настройки положения камеры.
Дополнительно - при нажатии на эту кнопку появляется форма
Свойства камеры с дополнительными параметрами камеры.
Форма «свойства камеры».
Радиус - расстояние от камеры до точки, на которую она направлена.
Шаг изменения радиуса - приращение радиуса камеры при однократном нажатии кнопки “-” на вкладке “Камера” формы “Опции” (или уменьшение при однократном нажатии кнопки “+”).
Форма «опции». вкладка «материалы».
В данном меню отображаются параметры материала
стола, на котором стоит сцена.
Цвет - цвет материала стола.
Коэф. диффузного отражения - коэффициент Kd материала стола (см. раздел 2.2.1).
Текстура - если галочка установлена, то на столе будет отображаться текстура
Выбрать текстуру - выбор файла изображения (*.bmp), который будет использоваться как текстура стола.
Дополнительно - при нажатии на эту кнопку появляется форма Свойства стола с дополнительными параметрами материала стола.
Форма «свойства стола».
Коэффициент блика - коэффициент KS материала стола (см. раздел 2.2.1).
Размытость блика - показатель степени p материала стола.
Повторения текстуры - сколько раз текстура стола
будет повторяться вдоль осей OX и OY.
Форма «опции». Вкладка «системные».
На этой вкладке можно настраивать алгоритмы,
реализованные в программе.
Глубина рекурсии - этот параметр устанавливает глубину рекурсии в алгоритме трассировки. При бОльших значениях этого параметра качество сгенерированного изображения улучшается.
ВНИМАНИЕ!
Глубина рекурсии СИЛЬНО влияет на скорость генерации изображения. Не рекомендуется ставить значения этого параметра больше 10.
Анитиалиазинг - включение алгоритма сглаживания изображения.
Тип тени - выбор алгоритма построения теней.
4. ИССЛЕДОВАТЕЛЬСКАЯ ЧАСТЬ
Исследования проводились на компьютере со следующей конфигурацией:
CPU - Intel Core 2 Duo T5850- 2048Mb
DDR2 - Nvidia GForce 9300M 256Mb- Windows 7
4.1 Зависимость времени генерации от
глубины рекурсии
В этом тесте исследовалась зависимость времени
генерации изображения от глубины рекурсии. Исследования проводились для сцены
освещенной одним источником света.- время генерации без тени в секундах.- время
генерации со сплошной тенью в секундах.- глубина рекурсии.
4.2 Зависимость времени генерации от
количества источников
4.3 Анализ результатов исследований
Из первого исследования видно, что время
генерации сильно вырастает с количеством уровней рекурсии. Это хорошо
соответствует теории, т.к. количество лучей растет с увеличением глубины
рекурсии. Надо заметить, что для сцен с маленьким
количеством полигонов нет необходимости задавать большие значения максимальной
глубины рекурсии, т.к. разница в качестве сгенерированного изображения будет
несущественна. Во втором исследовании показано, что зависимость
времени генерации от количества источников света линейна. Из полученных
значений можно вычислить время, необходимое для расчета одного источника. На
машине, на которой проводились исследования, при глубине рекурсии 5 это время
примерно равно 0,5 секунды. ЗАКЛЮЧЕНИЕ
В этой программе были продемонстрированы
результаты роботы алгоритма генерации реалистичных изображений - обратной
трассировки лучей. Данная реализация демонстрирует возможности
алгоритма строить изображения близкие к фотореалистичным. Трассировка является одним
из самых совершенных алгоритмов генерации реалистичных изображений. Качество
получаемого изображения несравнимо лучше, чем качество изображения, полученного
с помощью таких алгоритмов, как Z-буфер. Однако требования к вычислительным
мощностям, необходимым для генерации одного кадра изображения намного выше, чем
в том же Z-буфере. На сегодняшний день в реальном времени алгоритм обратной
трассировки лучей используют лишь в исследовательских целях на сверхмощных
компьютерах, недоступных простому пользователю. Безусловно, есть энтузиасты,
которые создают 3D игры и прочие графические приложения в реальном времени, в
основе которых лежит алгоритм обратной трассировки лучей, но как правило они
имеют крайне низкий показатель FPS, или в основе всех объектов на сцене лежит
сфера - самая простая для трассировки лучей поверхность. Но для того, чтобы
этот алгоритм стало выгодно использовать в массовых проектах, типа 3D игр,
необходим заметный прорыв в области аппаратной части настольных компьютеров. Даже на примере компьютерных игр можно легко
проследить избыточность алгоритма обратной трассировки лучей. Ведь игрок,
будучи увлеченным игровым процессом, навряд ли станет любоваться геометрически
правильной отрисовкой теней и отражений игровых объектов. В этом плане приближенная
рисовка с помощью полигонов сегодня значительно выигрывает, потому что не
требует мощного компьютера, а результаты дает приближенные к реальности. Также считается, что алгоритм трассировки лучей
идеален для изображений искусственных объектов с геометрически простыми
формами, например, автомобили, самолеты, здания и пр. Генерация таких объектов,
как человеческое лицо, шерсть животных или лесной массив - это крайне трудная
для алгоритма задача, которая повышает итак немалые требования к аппаратной части
компьютера. Однако уже сегодня можно увидеть исследования на
тему реализации алгоритма обратной трассировки лучей в реальном времени. Как
правило, в таких проектах в качестве сцены используют какой-либо автомобиль. Но
уже достигнута абсолютная фотореалистичность изображения, и к тому, же на
генерацию отдельного кадра уходит очень малое время. Конечно, эти проекты
реализованы на сверхмощных компьютерах, но не за горами день, когда такие 3D
приложения станут доступны и для рядового пользователя. СПИСОК ЛИТЕРАТУРЫ
1. Роджерс
Д. Алгоритмические основы машинной графики: пер. с англ.- М.: Мир, 1989.- 512
с. Порев
В. Н. Компьютерная графика. - СПб.: БХВ-Петербург, 2002. - 432 с. Никулин
Е.А. Компьютерная геометрия и алгоритмы машинной графики. СПб.: БХВ-Петербург,
2003. - 560 с. Эйнджел
Э. Интерактивная компьютерная графика. - «Вильямс», 2001. - 592 с.: ил. -
Парал. Тит. С англ. Авдеева
С.М., Куров А.В. Алгоритмы трехмерной машинной графики: Учебное пособие. - М.:
Изд-во МГТУ им. Н.Э. Баумана, 1996. - 60 с.
Отрисовка сцен, состоящих из, может быть, тысяч пересекающихся сфер. В реальности сцена может быть бесконечна, имеется в виду только видимая область.
Покадровый расчёт всей освещённости и затенённости. Все источники света - динамические (даже статические), поскольку они на самом деле динамические, только не изменяющие положение от кадра к кадру.
Попиксельный расчёт освещённости и попиксельное наложение теней, естественно, динамических.
Рендеринг мягких теней на основе физического приближения объёмных источников света. То есть, граница тени не резкая, а сильно размытая, степень размытости можно регулировать. Правда, это не совсем настоящие физически достоверные мягкие тени, а приближённые.
До 8 источников света может освещать одну сферу, соответственно, одна сфера может отбрасывать до 8 теней. Это не принципиальное ограничение, просто, когда в одной области много источников света, всё, конечно, сильно замедляется.
Поддержка точечных источников света и бесконечно удалённых источников света типа солнца. Как правило, сцену освещает один "солнечный" источник света, и несколько локальных.
Полностью динамическая сцена, то есть, положение объектов может меняться произвольным образом.
Наложение и билинейная фильтрация текстур.
Ограниченное использование прозрачных сфер с динамическим коэффициентом прозрачности.
Не очень изысканное изображение поверхности планеты в виде одной большой сферы, образующей эффект горизонта, когда объекты, находящиеся далеко, скрываются за линией горизонта.
http://www.art-render.com/
Сайт производителей "ускорителей рейтрейсинга" для оптимизации рендеренга в 3DMax и других графических редакторах. Ускоритель - это набор нескольких, от 8, оптимизированных для рейтрейсинга процессоров. Они умеют выполнять типичную для трассировки операцию - находить пересечение луча с треугольником - за один такт. Но, по-видимому, работают на не очень высокой частоте. Ускорение достигается за счёт параллельной работы. Сейчас на сайте трудно найти цены, но раньше я их видел, они совсем не маленькие.
http://www.acm.org/tog/resources/RTNews/html
Обширный список разнообразных ресурсов на тему трассировки лучей.
http://www.realstorm.com/
Движок на основе трассировки лучей. Позволяет рисовать в реальном времени большое количество типичных для трассировки эффектов, отражения и преломления света, например. Но работает в небольших разрешениях и использует аппроксимацию. На основе движка построена игра - симулятор боулинга.
http://www.kge.msu.ru/workgroups/compcenter/dmitri/projects/sphericworld/index.htm
http://www.kge.msu.ru/workgroups/compcenter/dmitri/projects/polyworld/index.htm
Ещё один проект, посвящённый методу трассировки лучей. Реализован сферический и полигональный рейтрейсер, строящий очень качественные реалистичные изображения, но медленно в больших разрешениях.
http://www.virtualray.ru/
Это, собственно, сайт, посвящённый предмету статьи - движку VirtualRay и игре AntiPlanet - первому 3D shooter на основе ray trace движка.
Которая должна наконец-то принести метод трассировки лучей в игры. Трассировка лучей (рейтрейсинг) - метод далеко не новый. Применительно к играм о нём говорили ещё лет 20 назад, а сам термин относительно компьютерной графики возник в 1982 году, и с тех пор в играх метод так и не появился. Но что это вообще такое?
Трассировка лучей - это метод построения трёхмерных моделей, в котором используется принцип, аналогичный реальным физическим процессам. То есть для построения объекта система отслеживает траекторию виртуального луча от экрана к этому самому объекту.
В реальности мы видим не объекты сами по себе, а отражённый от них свет. Исключением являются объекты, которые сами служат источниками света. Метод трассировки лучей использует примерно те же принципы применительно к виртуальной среде.
Проблема в том, что такой метод получается крайне затратным с точки зрения требований к аппаратным ресурсам. Если при использовании привычных методов рендеринга тот же цвет или прозрачность материала объекта задаются изначально, а отражения и тени эмулируются в том числе посредством шейдеров и прочих ухищрений, то в случае с рейтрейсингом эти характеристики определяются именно в процессе взаимодействия тех самых виртуальных лучей с объектом, как и в реальности. Это требует просто колоссальных затрат со стороны GPU даже в случае каких-то отдельных объектов, не говоря уже об использовании рейтрейсинга в играх в качестве основного метода построения объектов.
Для примера, для построения изображения разрешением 1024 х 768 пикселей посредством рейтрейсинга необходимо сформировать 768 432 луча. При этом каждый луч может как отражаться, так и преломляться, что в итоге приводит к увеличению количества трассируемых лучей в несколько раз. И если в случае обычных методов рендеринга необходимые полигоны просто нужно успеть отрисовать, то в случае рейтрейсинга каждый луч требует постоянных математических расчётов, начиная с момента его испускания.
Именно по этой причине рейтрейсинг уже давно используется там, где не требуется работа метода в режиме реального времени. В играх же нам как раз нужно последнее. Nvidia утверждает, что ранее не существовало графических адаптеров, имеющих достаточную производительность для такой работы. А теперь вроде есть Volta, которая должна аппаратно ускорять трассировку лучей. Правда, никаких подробностей на этот счёт нет. Пока совершенно неясно, насколько активно разработчики игр смогут использовать рейтрейсинг, и что мы, как игроки, получим на выходе. Но приблизительно понять это можно уже сейчас. К примеру, по видео ниже.
Это ожидаемая многими Metro Exodus. Она будет первой игрой AAA-класса, использующей технологию RTX. Но видите ли вы на видео что-то необычное? Если не знать, куда смотреть, большинство просто не поймёт, что данный ролик демонстрирует нам ожидаемую последние пару десятков лет технологию. На самом же деле рейтрейсинг в Metro будет использоваться для некоторых эффектов глобального освещения. Если точнее, для модели затенения ambient occlusion и для непрямого освещения indirect lighting. При этом классическая растеризация никуда не денется.
Таким образом, можно сделать вывод, что, несмотря на то, что внедрение рейтрейсинга в игры можно назвать революцией, пока это очень маленькая и незаметная революция. Эффекты на основе трассировки лучей мы будем видеть очень локально. Это будет касаться либо освещения, либо отражения, включая зеркала. Вероятно, что-то более глобальное можно будет увидеть лет через пять, а то и больше.
Думаю, стоит также отдельно оговорить момент касательно технологии Nvidia RTX и карт Volta. Как известно, в продаже имеется лишь две модели, использующие GPU GV100 - это ускоритель Tesla V100 и более близкий к народу, но безумно дорогой адаптер Titan V. Несомненно, в ближайшее время на рынок выйдут массовые видеокарты с GPU Volta, но ведь они достаточно долгое время будут в меньшинстве. Особенно, учитывая, что текущих адаптеров Pascal большинству хватает с головой. Да и майнинговую лихорадку, которая может испортить выход новинок, сбрасывать со счетов не стоит. Так неужели насладиться рейтрейсингом в играх в ближайшие пару лет смогут лишь владельцы новых карт Nvidia? Вовсе нет. Да, Nvidia говорит о неких возможностях GPU Volta аппаратно ускорять трассировку лучей, хотя подробностей пока ноль. Но выводить на потребительский рынок технологию, которая недоступна массам - глупость. Поэтому у нас есть Microsoft DirectX Raytracing (DXR) - набор новых инструментов и методов для API DirectX 12. Именно он будет отвечать как за аппаратную, так и за сугубо программную реализацию рейтрейсинга в играх. Последний вариант как раз интересен всем тем, у кого не будет видеокарт Volta.
Однако тут возникает вопрос касательно оптимизации и производительности. Приведу пример. Многие знают технологии Nvidia HairWorks и AMD TressFX Hair, которые позволяют более реалистично отрисовать волосы и шерсть. И эти технологии оптимизированы под адаптеры GeForce и Radeon соответственно. При этом на «чужих» видеокартах они прекрасно работают, только сильнее просаживают fps. Аналогичная ситуация будет и с рейтрейсингом в играх. Вопрос лишь в том, насколько сильно у адаптеров, не относящихся к поколению Volta, будет просаживаться fps при активации эффектов, связанных с трассировкой лучей. Это, к слову, ещё один довод в пользу того, что в ближайшие годы рейтрейсинг в играх будет в зачаточном виде.
Что касается AMD, компания на днях также представила собственный движок трассировки лучей - Radeon Rays 2.0. Только он базируется уже на основе API Vulkan. При этом ни о каких ограничениях в поколении GPU указано не было.
Подводя итог, можно сказать, что для большинства трассировка лучей в играх в ближайшие годы либо будет недоступна из-за недостаточно производительных видеокарт, либо пройдёт мимо попросту по той причине, что пользователь не заметит и не поймёт, где именно она применяется в той или иной сцене той или иной игры. Хотя, кто знает, возможно, всё будет совершенно иначе.
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter .