DirectX Raytracing: трассировка лучей в реальном времени. Что такое трассировка лучей в играх? Все, что вам нужно знать о большом графическом скачке

Я знаю, это немного разочаровывает. Где отражения, тени и красивый внешний вид? Мы всё это получим, ведь мы пока только начали. Но это хорошее начало - сферы выглядят как круги, а это лучше, чем если бы они выглядели как кошки. Они не выглядят как сферы потому, что мы упустили важный компонент, позволяющий человеку определять форму объекта - то, как он взаимодействует со светом.

Освещение

Первым шагом для добавления «реализма» нашему рендерингу сцены будет симуляция освещения. Освещение - это безумно сложная тема, поэтому я представлю очень упрощённую модель, достаточную для наших целей. Некоторые части этой модели даже не являются приближением к физическим моделям, они просто быстры и хорошо выглядят.

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

Во-первых, мы объявим, что всё освещение имеет белый цвет. Это позволит нам охарактеризовать любой источник освещения единственным действительным числом i, называемым яркостью освещения. Симуляция цветного освещения не так сложна (необходимо только три значения яркости, по одному на канал, и вычисление всех цветов и освещения поканально), но чтобы сделать нашу работу проще, я не буду его делать.

Во-вторых, мы избавимся от атмосферы. Это значит, что освещение не становятся менее яркими, независимо от их дальности. Затухание яркости света в зависимости от расстояния реализовать тоже не слишком сложно, но для ясности мы пока его пропустим.

Источники освещения

Свет должен откуда-то поступать. В этом разделе мы зададим три различных типа источников освещения.

Точечные источники

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

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

Давайте зададим вектор как направление из точки P в сцене к источнику освещения Q. Этот вектор, называемый световым вектором , просто равен . Заметьте, что поскольку Q фиксирована, а P может быть любой точкой сцены, то в общем случае будет разным для каждой точки сцены.

Направленные источники

Если точечный источник является хорошей аппроксимацией лампы накаливания, то что может служить аппроксимацией Солнца?

Это хитрый вопрос, и ответ зависит от того, что вы хотите отрендерить.

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

Однако если в вашей сцене действие происходит на Земле, то это не слишком хорошее приближение. Солнце находится так далеко, что каждый луч света будет на самом деле иметь одинаковое направление (Примечание: эта аппроксимация сохраняется в масштабе города, но не на более дальних расстояниях - на самом деле. древние греки смогли с удивительной точностью вычислить радиус Земли на основании разных направлений солнечного света в различных местах.). Хотя это можно аппроксимировать это с помощью точечного источника, сильно удалённого от сцены, это расстояние и расстояние между объектами в сцене настолько отличаются по величине, что могут появиться ошибки точности чисел.

Для таких случаев мы зададим направленные источники освещения . Как и точечные источники, направленный источник имеет яркость, но в отличие от них, у него нет позиции. Вместо неё у него есть направление . Можно воспринимать его как бесконечно удалённый точечный источник, светящий в определённом направлении.

В случае точечных источников нам нужно вычислять новый световой вектор для каждой точки P сцены, но в этом случае задан. В сцене с Солнцем и Землёй будет равен .

Окружающее освещение

Можно ли смоделировать любое освещение реального мира как точечный или направленный источник? Почти всегда да (Примечание: но это необязательно будет просто; зональное освещение (представьте источник за рассеивателем) можно аппроксимировать множеством точечных источников на его поверхности, но это сложно, более затратно по вычислениям, а результаты оказываются неидеальными.). Достаточно ли этих двух типов источников для наших целей? К сожалению, нет.

Представьте, что происходит на Луне. Единственным значимым источником освещения поблизости является Солнце. То есть «передняя половина» Луны относительно Солнца получает всё освещение, а «задняя половина» находится в полной темноте. Мы видим это с разных углов на Земле, и этот эффект создаёт то, что мы называем «фазами» Луны.

Однако ситуация на Земле немного отличается. Даже точки, не получающие освещения непосредственно от источника освещения, не находятся полностью в темноте (просто посмотрите на пол под столом). Как лучи света достигают этих точек, если «обзор» на источники освещения чем-то перекрыт?

Как я упомянул в разделе Цветовые модели , когда свет падает на объект, часть его поглощается, но остальная часть рассеивается в сцене. Это значит, что свет может поступать не только от источников освещения, но и от других объектов, получающих его от источников освещения и рассеивающих его обратно. Но зачем останавливаться на этом? Рассеянное освещение в свою очередь падает на какой-нибудь другой объект, часть его поглощается, а часть снова рассеивается в сцене. При каждом отражении свет теряет часть своей яркости, но теоретически можно продолжать ad infinitum (Примечание: на самом деле нет, потому что свет имеет квантовую природу, но достаточно близко к этому.).

Это значит, что нужно считать источником освещения каждый объект . Как можно представить, это сильно увеличивает сложность нашей модели, поэтому мы не пойдём таким путём (Примечание: но вы можете хотя бы загуглить Global Illumination и посмотреть на прекрасные изображения.).

Но мы всё равно не хотим, чтобы каждый объект был или освещён напрямую, или был полностью тёмным (если только мы не рендерим модель Солнечной системы). Чтобы преодолеть эту преграду, мы зададим третий тип источников освещения, называемый окружающим освещением , которое характеризуется только яркостью. Считается, что оно носит безусловный вклад освещения в каждую точку сцены. Это очень сильное упрощение чрезвычайно сложного взаимодействия между источниками освещения и поверхностями сцены, но оно работает.

Освещённость одной точки

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

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

Итак, что произойдёт, когда луч света с направлением из направленного или точечного источника падает на точку P какого-нибудь объекта в нашей сцене?

Интуитивно мы можем разбить объекты на два общих класса, в зависимости от того, как они ведут себя со светом: «матовые» и «блестящие». Поскольку большинство окружающих нас предметов можно считать «матовыми», то с них мы и начнём.

Диффузное рассеяние

Когда луч света падает на матовый объект, то из-за неровности его поверхности на микроскопическом уровне, он отражает луч в сцену равномерно во всех направлениях, то есть получается «рассеянное» («диффузное») отражение.

Чтобы убедиться в этом, внимательно посмотрите на какой-нибудь матовый объект, например, на стену: если двигаться вдоль стены, её цвет не меняется. То есть, видимый вами свет, отражённый от объекта, одинаков вне зависимости от того, в какое место объекта вы смотрите.

С другой стороны, количество отражённого света зависит от угла между лучом света и поверхностью. Интуитивно это понятно - энергия, переносимая лучом, в зависимости от угла должна распределиться по меньшей или большей поверхности, то есть энергия на единицу площади, отражённая в сцену, будет соответственно выше или ниже:

Чтобы выразить это математически, давайте охарактеризуем ориентацию поверхности по её вектору нормали . Вектор нормали, или просто «нормаль» - это вектор, перпендикулярный поверхности в какой-то точке. Также он является единичным вектором, то есть его длина равна 1. Мы будем называть этот вектор .

Моделирование диффузного отражения

Итак, луч света с направлением и яркостью падает на поверхность с нормалью . Какая часть отражается обратно сцену как функция от , и ?

Для геометрической аналогии давайте представим яркость света как «ширину» луча. Его энергия распределяется по поверхности размером . Когда и имеют одно направление, то есть луч перпендикулярен поверхности, , а это значит, что энергия, отражённая на единицу площади равна падающей энергии на единицу площади; < . С другой стороны, когда угол между и приближается к , приближается к , то есть энергия на единицу площади приближается к 0; . Но что происходит в промежутках?

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

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

Луч света с шириной падает на поверхность в точке под углом . Нормаль в точке равна , а энергия, переносимая лучом, распределяется по . Нам нужно вычислить .

Один из углов равен , а другой - . Тогда третий угол равен . Но нужно заметить, что и тоже образуют прямой угол, то есть тоже должны быть . Следовательно, :

Давайте рассмотрим треугольник . Его углы равны , и . Сторона равна , а сторона равна .

И теперь… тригонометрия спешит на помощь! По определению ; заменяем на , а на , и получаем


что преобразуется в
Мы почти закончили. - это угол между и , то есть можно выразить как
И наконец
Итак, мы получили очень простое уравнение, связывающее отражённую часть света с углом между нормалью к поверхности и направлением света.

Заметьте, что при углах больше значение становится отрицательным. Если мы не задумываясь используем это значение, то в результате получим источники света, вычитающие свет. Это не имеет никакого физического смысла; угол больше просто означает, что свет на самом деле достигает задней части поверхности, и не вносит свой вклад в освещение освещаемой точки. То есть если становится отрицательным, то мы считаем его равным .

Уравнение диффузного отражения

Теперь мы можем сформулировать уравнение для вычисления полного количества света, полученного точкой с нормалью в сцене с окружающим освещением яркостью и точечных или направленных источников света с яркостью и световыми векторами или известными (для направленных источников), или вычисленными для P (для точечных источников):
Стоит снова повторить, что члены, в которых не должны прибавляться к освещённости точки.

Нормали сферы

Здесь только отсутствует единственная мелочь: откуда берутся нормали?

Этот вопрос намного хитрее, чем кажется, как мы увидим во второй части статьи. К счастью, для разбираемого нами случая есть очень простое решение: вектор нормали любой точки сферы лежит на прямой, проходящей через центр сферы. То есть если центр сферы - это , то направление нормали в точки равно :

Почему я написал «направление нормали», а не «нормаль»? Кроме перпендикулярности к поверхности, нормаль должна быть единичным вектором; это было бы справедливо, если бы радиус сферы был равен , что не всегда верно. Для вычисления самой нормали нам нужно разделить вектор на его длину, получив таким образом длину :


Это представляет в основном теоретический интерес, потому что записанное выше уравнение освещения содержит деление на , но хорошим подходом будет создание «истинных» нормалей; это упростит нам работу в дальнейшем.

Рендеринг с диффузным отражением

Давайте переведём всё это в псевдокод. Во-первых, давайте добавим в сцену пару источников освещения:

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) } }
Вот как выглядит наша сцена при наблюдении из другого положения и при другой ориентации:

Куда двигаться дальше

Мы закончим первую часть работы кратким обзором некоторых интересных тем, которые мы не исследовали.

Оптимизация

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

Параллелизация

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

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

Кэширование значений

Рассмотрим значения, вычисляемые IntersectRaySphere , на который трассировщик лучей обычно тратит большинство времени:

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

Теги: Добавить метки

Трассировка лучей и растеризация — в чем разница?

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

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

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

Процесс затенения (shading ) заключается в расчете количества освещения для пикселя с учетом наложения одной или нескольких текстур на пиксель, что и определяет его конечный цвет. Все это требует большого количества вычислений, ведь в сценах современных игр содержится по несколько миллионов полигонов и по несколько миллионов пикселей в экранах высокого разрешения, а обновление информации на экране должно быть с частотой хотя бы 30 кадров в секунду, а лучше — 60 FPS. Не говоря уже о шлемах виртуальной реальности, где нужно одновременно рисовать изображения для двух глаз с частотой в 90 FPS.

Но так как графические процессоры работают на очень высокой тактовой частоте и имеют большое количество аппаратных блоков, специализированных на определенных вычислениях, а растеризация очень хорошо поддается распараллеливанию, то особых проблем с производительностью рендеринга нет, и подавляющее большинство компьютерных 3D-игр использует именно растеризацию. В реальности дела обстоят несколько сложнее, так как используется множество дополнительных оптимизаций для того, чтобы не отрисовывать множество невидимых треугольников, но суть растеризации в общем и целом именно такова.

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

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

У трассировки же лучей основная идея совершенно другая, но в теории чуть ли не проще. При помощи трассировки имитируется распространение лучей света по 3D-сцене. Трассировка лучей может выполняться в двух направлениях: от источников света или от каждого пикселя в обратном направлении, далее обычно определяется несколько отражений от объектов сцены в направлении камеры или источника света, соответственно. Просчет лучей для каждого пикселя сцены менее требователен вычислительно, а проецирование лучей от источников света дает более высокое качество рендеринга.

Обратная трассировка была впервые описана в 1969 году сотрудником компании IBM в работе «Some Techniques for Shading Machine Renderings of Solids» и эта техника просчитывает путь луча света для каждого пикселя на экране в зависимости от 3D-моделей в сцене. Через 10 лет произошел еще один рывок в технологиях, когда исследователь Turner Whitted (ныне работающий в Nvidia Research, к слову) опубликовал работу «An Improved Illumination Model for Shaded Display» , показавшую как можно при трассировке рассчитывать тени, отражение и преломление света.

Еще пара работ в 1980-х дополнительно описала основы трассировки лучей для компьютерной графики, которые вылились в целую революцию построения синтетического изображения в киноиндустрии. Так, в 1984-м несколько сотрудников Lucasfilm описали, как при помощи трассировки лучей создать такие эффекты, как смазывание в движении (motion blur), глубина резкости (depth of field), мягкие тени, размытые отражения и преломления. Еще через пару лет профессор Калифорнийского технологического института Jim Kajiya в своей работе «The Rendering Equation» описал более точный способ рассеивания света в сцене. И с тех пор трассировка лучей в киноиндустрии применялась буквально повсеместно.

Итак, в распространенном методе обратной трассировки лучей, для каждого пикселя на экране проводится воображаемый луч от камеры до объекта в сцене. Таким образом имитируется луч света, пришедший в камеру от источника света по этому направлению, и первое пересечение с объектом используется для определения цвета пикселя. Первичные лучи определяют видимость объектов (вроде Z-буфера в растеризации), а для определения цвета нужно провести дальше вторичные лучи от точек пересечения к разным источникам света (если лучи блокируются объектом, то источник света не будет влиять на освещение пикселя), а совокупность вторичных лучей определяет освещение, попадающее на пиксель.


Но все самое интересное происходит еще дальше — для достижения фотореалистичности нужно учитывать характеристики материалов в виде количества отражаемого и преломляемого ими света, и для расчета цвета пикселя нужно провести еще лучи отражения и преломления. На рисунке выше они не указаны, но их можно мысленно вообразить как лучи, отраженные от поверхности шара и преломленные ей. Такой улучшенный алгоритм трассировки лучей был изобретен уже несколько десятков лет назад, и эти дополнения стали большим шагом по увеличению реалистичности синтетической картинки. К сегодняшнему дню метод обрел множество модификаций, но в их основе всегда лежит нахождение пересечения лучей света с объектами сцены.

Первые практические опыты по реализации трассировки лучей в реальном времени начались довольно давно, на известной конференции SIGGraph подобные разработки появлялись нередко. Демонстрации трассировки в реальном времени появились еще в конце 80-х годов прошлого века и обеспечивали скорость в несколько кадров в секунду, используя высокооптимизированные техники и несколько вычислительных систем с общей памятью для обсчета. С тех пор появилось множество разработок, предназначенных для ускорения трассировки для работы, в том числе и на одном ПК.

Не говоря уже о многочисленных 3D-движках энтузиастов демо-сцены в конце 90-х и далее, которые воодушевились возможностями и принципиальной простотой метода, привнеся множество полезных оптимизаций в трассировку лучей. У нас на сайте в свое время была опубликована целая серия материалов, посвященных одному из программных движков трассировки лучей, весьма специфическому и с массой серьезных ограничений, не позволяющих создать на его основе серьезные игровые проекты:

Не отставали и производители аппаратного обеспечения, которые еще давно показывали на выставках экспериментальные прототипы ускорителей трассировки и оптимизированные для них демонстрационные программы. Так, в июне 2008 года компания Intel показала специальную версию игры Enemy Territory: Quake Wars (Quake Wars: Ray Traced) , использующую трассировку лучей при рендеринге в разрешении 1280×720 на скорости 15-30 кадров в секунду, что уже считается реальным временем. Та демонстрация не использовала аппаратные ускорители, а работала на 16 ядрах Xeon на частоте под 3 ГГц.

Проект Intel наглядно показывал плюсы рендеринга, использующего трассировку лучей, демонстрируя реалистичную воду, тени от объектов сквозь прозрачные поверхности, а также отражения. Развитием демонстрации стал проект Wolfenstein: Ray Traced , да и различные энтузиасты частенько берут движок серии Quake для добавления трассировки — так с подачи моддеров в Quake 2 появились реалистичные отражения, которые портил очень сильный шум и высочайшие системные требования.

А прототипы уже аппаратных ускорителей трассировки несколько лет (начиная с 2012-го и заканчивая 2016-м) показывала компания Imagination Technologies , предлагающая даже открытый API для трассировки лучей — OpenRL . Заявлялось, что аппаратный ускоритель разработки этой компании способен работать в Autodesk Maya и обеспечивать трассировку лучей в реальном времени. Впрочем, средств на продвижение аппаратного ускорения трассировки лучей у компании для успеха не хватило, как и «веса» этой компании на графическом рынке, чтобы быть его локомотивом. Да и демонстрационные программы были не самыми впечатляющими, честно говоря, хотя и показывали некоторые преимущества трассировки:

Гораздо лучше дело пошло у компании Nvidia , которая еще на SIGGraph 2009 анонсировала технологию OptiX , предназначенную для трассировки лучей в реальном времени на графических процессорах их производства. Новый API открыл доступ к выполнению трассировки лучей в профессиональных приложениях с необходимой гибкостью, в частности — двунаправленному path tracing и другим алгоритмам.

Основанные на технологии OptiX рендереры уже существуют для многочисленного профессионального ПО, вроде Adobe AfterEffects, Bunkspeed Shot, Autodesk Maya, 3ds max и других приложений, и используются профессионалами в работе. К рендерингу реального времени это можно отнести лишь с определенными допущениями, потому что при высокой частоте кадров получалась очень шумная картинка. Лишь через несколько лет индустрия вплотную подошла к применению аппаратного ускорения трассировки лучей уже в играх.

Плюсы и минусы трассировки лучей

Техника рендеринга с трассировкой лучей отличается высоким реализмом, по сравнению с растеризацией, так как она имитирует распространение лучей света очень похоже на то, как это происходит в реальности (естественно, все равно не на 100% точно). Трассировка позволяет воссоздать весьма реалистичные тени, отражения и преломления света, и поэтому она давно ценится в архитектурных приложениях и промышленном дизайне. Технология помогает специалистам этой сферы задолго до физического воплощения понять, как будут смотреться материалы при различном освещении в реальном мире.

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

Но важнее все же именно то, что метод имитирует реальное распространение лучей света, получая итоговую картинку более высокого качества, по сравнению с растеризацией. У растеризации есть явные недостатки — к примеру, не входящий в сцену объект не будет отрисовываться на GPU, но ведь он может отбрасывать видимую тень или должен быть виден в отражающей поверхности (зеркале), а оптимизации растеризации его отбросили и не приняли во внимание. Не говоря уже о том, что этот невидимый объект может сильно влиять на глобальное освещение сцены, отражая свет на видимые поверхности. Частично эти проблемы решаются, в частности, применение карт теней позволяет отрисовать тени от невидимых в сцене объектов, но отрисованная в результате картинка все равно далека от идеала. И дело тут в самом принципе, ведь растеризация работает совсем не так, как человеческое зрение.

Такие эффекты, как отражения, преломления и тени, довольно сложные для качественной реализации при растеризации, являются натуральным результатом работы алгоритма трассировки лучей. Возьмем отражения — это лишь одна из областей, в которых метод трассировки лучей заметно лучше растеризации. В современных играх отражения обычно имитируются при помощи карт окружения (environment map, статических или динамических) или отражениями в экранном пространстве (Screen-Space ), которые дают неплохую имитацию отражений в большинстве случаев, но все же имеют очень большие ограничения, в частности — не подходят для близко расположенных объектов.

Расчет отражений в экранном пространстве позволяет получить более-менее похожие на правду отражения при некоторых ограничениях, зато при аппаратном ускорении на GPU с использованием растеризации. А при трассировке лучей отражения всегда отображаются идеально без необходимости в дополнительных сложных алгоритмах. Еще одним важным преимуществом трассировки является вывод отражений частей одного и того же объекта друг на друге (к примеру, чтобы ручка чайника или его носик отражались на нем самом), что куда сложнее сделать при помощи растеризации.

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

Ну и последний (для начала) пример — отрисовка теней. При растеризации в большинстве случаев используются карты теней (shadow mapping ), которые также основаны на растеризации, просто рендеринг делается из другой точки сцены и с другими параметрами. Силуэты объекта рисуются в отдельный буфер от источника света, содержимое буфера фильтруется и накладывается на поверхность, куда тень должна отбрасываться. У таких методов есть несколько проблем, включая неровности («лесенки») на контурах, которые вы все видели в играх, а также увеличенный расход видеопамяти. Трассировка же лучей позволяет решить проблему теней автоматически, не требуя дополнительных алгоритмов и объемов памяти. Более того, в случае хака растеризации получится в любом случае некорректная физически тень, а вот отрисованная при помощи трассировки лучей мягкая тень будет реалистичной.

Но есть у трассировки лучей и недостаток. Один, но очень важный — отрисовать все вышеописанное с вычислительной точки зрения в несколько раз сложнее. Низкая производительность на существующем «железе» — главный недостаток метода трассировки, который долгое время перечеркивал все его плюсы. Нахождение пересечения лучей с объектами сцены не ускоряется столь же легко, как сравнительно простые операции при растеризации треугольников, для которых много лет используются специальные 3D-ускорители, именно поэтому в графике реального времени до сих пор используется метод растеризации, который позволяет довольно быстро нарисовать картинку, хоть и несколько уступающую в качестве полноценной трассировке, но достаточно реалистичную при этом.

При трассировке вам нужно просчитать тысячи лучей для каждого источника освещения, большая часть из которых будет слабо влиять на итоговую картинку, поэтому нужны как дополнительные оптимизации для алгоритма трассировки лучей, так и новое аппаратное обеспечение, способное ускорять соответствующие операции. Плюс к этому, само по себе использование трассировки не гарантирует фотореализма. Если применять простые алгоритмы, то результат будет неплохим, но все равно недостаточно реалистичным, а для полноценной имитации реальности нужно применять дополнительные техники, вроде photon mapping и path tracing , которые точнее имитируют распространение света в мире.

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

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

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

Есть множество методов для ускорения трассировки, самые производительные алгоритмы трассировки лучей обрабатывают лучи не по одному, а используют наборы лучей, что ускоряет процесс обработки лучей одинакового направления. Такие оптимизации отлично подходят для исполнения на современных SIMD-блоках CPU и на GPU, они эффективны для основных сонаправленных лучей и для теневых лучей, но все равно не подходят для лучей преломления и отражения. Поэтому приходится серьезно ограничивать количество лучей, рассчитываемых для каждого пикселя сцены, а повышенную «шумность» картинки убирать при помощи специальной фильтрации.

Кроме этого, метод трассировки лучей требует подходящей структуры данных для хранения элементов сцены, и она может иметь определяющее значение для производительности. Какие-то структуры лучше подходят для статических данных, другие — для динамически изменяющихся. Так что трассировка лучей только при поверхностном взгляде кажется простым и максимально элегантным методом, но для получения от нее нужной производительности придется очень много поработать для оптимизации — не меньше, чем для имитации сложных эффектов при растеризации. И эта работа только началась, по сути.

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

Да, трассировка лучей может дать лучшее качество, чем растеризация, но какими силами? Если стремиться к полной реалистичности, то полноценная трассировка с расчетом множества лучей для освещения и отражений, а также комбинацией техник вроде radiosity и photon mapping , будет сверхтребовательной к вычислительной мощности. Зачастую даже офлайновые рендеры, работающие не в реальном времени, используют упрощения. Конечно, через какое-то время достаточно высокая вычислительная мощность станет доступна для того, чтобы получить преимущество перед растеризацией в том числе и по производительности, но пока что мы еще очень далеки от этого момента.

Даже при офлайновом рендеринге для киноиндустрии при росте вычислительной мощности время рендеринга со временем не снижается, так как аппетиты художников растут еще быстрее! И даже лидирующие в производстве анимационных картин компании, вроде Pixar , стараются оптимизировать процесс рендеринга, используя трассировку лучей только для части эффектов — именно из-за значительного влияния на производительность. Так что надо понимать, что времена полноценной трассировки для всей сцены в играх реального времени еще очень далеки. И для полноценного рендеринга в реальном времени методом трассировки лучей в играх вычислительных мощностей пока что точно не хватит. Это длинный путь даже при том развитии GPU, которое продолжается до сих пор.

Но в любом случае, именно трассировка лучей является тем самым физически корректным путем, который способен решить множество больших и мелких проблем существующего подхода. При помощи различных хаков и трюков, применяемых сейчас в растеризации, можно добиться неплохого результата, но это точно нельзя назвать универсальным и идеальным методом для визуализации 3D-графики. Уже довольно скоро, стремясь к реализму, 3D-разработчики приложений реального времени достигнут предела существующего метода растеризации, и им придется перейти на метод с продвинутой моделью освещения, похожей на то, что происходит в реальности. Скорее всего, это будет именно трассировка лучей. Но так как трассировка лучей весьма затратный метод и ее вряд ли потянут даже самые мощные системы, то поначалу стоит рассчитывать на гибридные методы рендеринга, сочетающие производительность растеризации и качество трассировки лучей.

Гибридный рендеринг для переходного периода

Исходя из требовательности трассировки лучей даже с малым количеством рассчитываемых лучей для каждого пикселя, этот метод вряд ли можно применять исключительно, и пока что он не заменит растеризацию. Но есть вариант смешения двух методик. К примеру, основу геометрии можно растеризовать с высокой производительностью, а затем при помощи трассировки лучей просчитывать только мягкие тени и отражения. Хотя растеризация продолжит играть важнейшую роль и в ближайшие годы с появлением гибридного рендеринга, доля алгоритмов трассировки лучей в таких движках будет постепенно расти исходя из роста вычислительных возможностей будущих GPU.

Такой подход давно используется в тех же мультфильмах компании Pixar , несмотря на то, что у них в требованиях вроде бы нет жестких ограничений по времени рендеринга. Тем не менее, проще и быстрее отрисовывать геометрию при помощи тех же микрополигонов системы рендеринга Reyes , а трассировку использовать только там, где нужны конкретные эффекты. Практически все анимационные фильмы студии Pixar использовали ранее микрополигоны и растеризацию, а трассировку лучей к движку рендеринга RenderMan добавили позднее для мультфильма «Тачки», где она использовалась избирательно — для расчета глобального затенения (ambient occlusion) и отрисовки отражений.

Но на деле гибридные решения не так просты, ведь для эффективной трассировки лучей нужно организовать структуру данных специальным образом, чтобы снизить количество проверок пересечения лучей с объектами сцены. Поэтому и при гибридном рендеринге придется создавать оптимизированную структуру данных. А в вопросе производительности большой проблемой является доступ к памяти, связанный с вторичными лучами, которые нужны при гибридном рендеринге. Получается, что при комбинации двух методов рендеринга объединяются и многие их недостатки, в частности — уходит простота метода трассировки лучей и высокая производительность растеризации.

Но когда достоинства все же перевешивают, то подобный гибридный подход имеет смысл. Уже сейчас доступно сочетание некоторых возможностей растеризации и трассировки, включая аппаратно-ускоренную на GPU подготовку карт освещения, рендеринг динамических карт освещения и части теней, отрисовку отражений и полупрозрачных объектов с преломлением. Уже это является большим достижением, так как подобный подход вот уже много лет был доступен лишь при офлайновом рендеринге. Еще в конце 90-х гибридный рендеринг применялся при создании анимационных фильмов, чтобы улучшить эффективность, а сейчас он становится доступным и для приложений реального времени.


Но это лишь самое начало перед грядущей «Золотой эрой» рендеринга реального времени. В будущем подобный гибридный подход разовьется в нечто большее, и вместо выборочных эффектов можно будет использовать полноценные техники с продвинутым освещением, затенением и различными сложными эффектами.

Примерно так же, как офлайновый рендеринг за несколько лет прошел путь от «Bug’s Life» к куда более сложным анимационным фильмам, вроде «Coco» , использующим уже полноценный path tracing с десятками, а то и сотнями просчитываемых лучей на пиксель. В отличие от прошлых лет, там уже не было карт теней, отдельных проходов для расчета освещения, а только полноценная трассировка — к этому же стремятся и разработчики игр, просто их путь будет несколько длиннее, но цель то одинакова.


А до того, как процесс перехода от растеризации к полной трассировке произойдет, нужно использовать гибридный рендеринг и во многом менять подход к разработке. Например, отдать часть работы по предварительной подготовке и «запекании» некоторых данных в GPU, переделать свой производственный конвейер и готовить движки рендеринга к тому, что все большая часть расчетов будет постепенно переходить на трассировку. А частичные преимущества трассировки получится использовать уже сейчас, хоть и с крайне малым количеством лучей на пиксель и с обязательным шумоподавлением.


Но даже при постепенном переходе к трассировке, не нужно отбрасывать необходимость оптимизаций, не специфичных для растеризации. Высокоуровневые оптимизации вроде уровней детализации (level of detail — LOD), отбрасывания невидимых поверхностей (occlusion culling), тайлинга и стриминга отлично будут работать и при трассировке лучей. И пока индустрия не перейдет к полноценной трассировке, нужно продолжать применять эффективные техники с использованием экранного пространства там, где необходима высокая производительность и не критично качество.


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

Даже обычные фильтры для подавления шумов с настройкой именно под особенности работы трассировки лучей работают неплохо, а если применить шумоподавление, использующее возможности нейросетей, которое уже демонстрировала та же Nvidia, да еще и аппаратно ускоренное на тензорных ядрах графических процессоров архитектуры Volta, то будущее гибридного рендеринга видится довольно ясным — по крайней мере, некоторые из эффектов, которые можно легко добавить в существующие движки (расчет теней или глобального освещения и затенения), использующие растеризацию, будут добавлены в игры уже довольно скоро.

Итак, очевидный способ применения гибридного рендеринга заключается в растеризации сцены и применении трассировки лучей лишь для части расчетов ее освещения, а также для расчетов отражений с преломлениями. Такой подход дает скорость растеризации и качество трассировки в виде аккуратной имитации освещения, в том числе глобального, отражений и преломлений лучей света и отрисовке оптически корректных теней. Более того, симуляция этих эффектов при помощи хаков в растеризации и их усложнение когда-то приведет к тому пределу, когда это станет настолько ресурсоемко, что проще будет заменить вычисления настоящей трассировкой лучей. И в целом это — единственно правильный путь, если смотреть в будущее развития графики.

DirectX Raytracing — стандартный API для трассировки лучей

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

Современные техники вроде отражений в экранном пространстве и имитации глобального освещения практически подвели растеризацию к ее практическим пределам, так как эти алгоритмы требуют хитрых хаков при обработке и сложных вычислений, порой выполняемых асинхронно с рендерингом. И в ближайшее время сложность и ресурсоемкость таких алгоритмов еще продолжит рост. Трассировка лучей же позволяет делать сложные эффекты простым способом, также открывая двери для внедрения совершенно новых техник, невозможных ранее при рендеринге реального времени. Но как этого добиться, если GPU умеют только растеризацию?

Текущая версия DirectX 12 лишь кажется довольно новой, а на деле этот графический API был анонсирован еще на выставке GDC 2014, а вышел публично в составе Windows 10 годом позже. До сих пор применение этой версии далеко от желаемого и так получилось сразу по многим причинам. Во-первых, цикл разработки игр и движков довольно большой, а то, что DirectX 12 работает только в последней версии Windows и имеет ограниченную поддержку в консолях нынешнего поколения, лишь уменьшает число доводов в пользу его использования на ПК. Тем не менее, применение низкоуровневого API мы уже увидели в нескольких играх, но что же дальше? А дальше линия развития DirectX резко повернула еще раз, представив средства для поддержки трассировки лучей.

В рамках конференции игровых разработчиков GDC 2018 компания Microsoft представила новое дополнение к DirectX API, в котором так или иначе поучаствовало множество партнеров, занимающихся разработкой программного и аппаратного обеспечения. Дополнение называется DirectX Raytracing и его имя говорит о том, что это — стандартный API для программной и аппаратной поддержки трассировки лучей в DirectX-приложениях, позволяющее разработчикам использовать алгоритмы и эффекты с применением упомянутой техники. DirectX Raytracing (далее DXR для краткости) обеспечивает стандартизированный подход для внедрения трассировки лучей, которая ускоряется при помощи графических процессоров. Это расширение сочетается с возможностями существующего DirectX 12 API, позволяя использовать как традиционную растеризацию, так и трассировку лучей, а также смешивать их в желаемых пропорциях.

Вся работа DXR API, связанная с трассировкой лучей, управляется при помощи списков команд, отправляемых приложением. Трассировка лучей тесно интегрирована с растеризацией и вычислительными командами и может запускаться многопоточно. Шейдеры трассировки лучей (целых пять новых типов шейдеров!) управляются аналогично вычислительным шейдерам, что позволяет использовать их параллельную обработку на GPU, управляя их выполнением на сравнительно низком уровне. Приложение при этом полностью отвечает за синхронизацию работы GPU и использованию его ресурсов, как при растеризации и вычислениях, что дает разработчикам контроль над оптимизацией выполнения всех типов работы: растеризация, трассировка лучей, вычисления, передача данных.

Разные виды рендеринга делят все ресурсы, такие как текстуры, буферы и константы, не требуя конвертации, переноса и дублирования для доступа из шейдеров трассировки. Ресурсы, хранящие специфические для трассировки лучей данные, такие как структуры ускорения (структуры данных, используемые для ускорения трассировки — поиск пересечений лучей и геометрии) и таблицы шейдеров (описывают связь между шейдерами трассировки лучей, ресурсами и геометрией), полностью управляются приложением, сам DXR API не делает никаких перемещений данных по своей воле. Шейдеры можно скомпилировать индивидуально или пакетно, их компиляция полностью управляется приложением и может быть распараллелена на несколько потоков CPU.

На самом высоком уровне DXR добавляет четыре новые концепции к DirectX 12 API:

  1. Структура ускорения (acceleration structure ) — это объект, представляющий 3D-сцену в формате, оптимальном для обсчета лучей на графических процессорах. Представленная в виде двухуровневой иерархии, эта структура обеспечивает оптимизированный просчет лучей на GPU и эффективное изменение динамических данных.
  2. Новый метод списка команд (command list ) под названием DispatchRays является основой для трассировки лучей в сцене. Именно таким образом игра передает рабочие задачи DXR в GPU.
  3. Набор новых типов шейдеров для создания лучей, которые определяют, что именно будет вычислять DXR. При вызове DispatchRays, запускается шейдер генерации лучей. При использовании новой функции TraceRay в HLSL, шейдер генерации лучей отправляет луч в сцену, и в зависимости от того, куда луч попадает в сцене, в точке пересечения может быть вызван один из нескольких шейдеров попадания (hit ) или промаха (miss ), что позволяет назначать для каждого объекта собственный набор шейдеров и текстур и создавать уникальные материалы.
  4. Состояние конвейера трассировки, добавленное к уже существующим состояниям графического и вычислительного конвейеров, переводящее шейдеры трассировки лучей и другие состояния, относящиеся к рабочим нагрузкам трассировки.

Таким образом, DXR не добавляет нового движка GPU к существующим в DirectX 12 графическому и вычислительному. Нагрузка от DXR может выполняться на уже существующих движках, так как DXR — это вычислительная задача, по сути. Задачи DXR представлены в виде вычислительных нагрузок потому, что графические процессоры в любом случае становятся все более универсальными и способны исполнять практически любые задачи, совсем не обязательно связанные с графикой, а в будущем основная часть фиксированных функций GPU вероятно будет заменена шейдерным кодом.

При использовании DXR, первым шагом является построение структур ускорения в двух уровнях. На нижнем уровне структуры, приложение определяет набор геометрических данных (вершинных и индексных буферов), определяющих объекты в сцене. На верхнем уровне структуры задается список описаний, содержащих ссылки на определенные геометрические данные, а также дополнительные данные, такие как матрицы преобразования, которые обновляются каждый кадр аналогично тому, как это делается в играх для динамического изменения объектов. Так обеспечивается эффективный обход большого количества сложной геометрии.

Второй шаг при использовании DXR — это создание состояния конвейера трассировки. Современные игры группируют вызовы отрисовки (draw calls ) для увеличения эффективности их исполнения в специальные группы — пакеты (batch ), например, отрисовывая все металлические объекты в одном батче, а все пластиковые — в другом. Но при трассировке невозможно заранее точно знать, на какой материал попадет конкретный луч, и батчи применить не получится. Вместо этого, состояние конвейера трассировки позволяет назначить несколько наборов шейдеров трассировки и текстурных ресурсов. Таким образом можно указать, к примеру, что все пересечения лучей с одним объектом должны использовать такой-то конкретный шейдер и такую-то текстуру, а пересечения с другим объектом — другой шейдер и другую текстуру. Это позволяет приложению использовать нужный шейдерный код с правильными текстурами для материалов, в которые попали лучи.

Последним шагом в работе DXR является вызов DispatchRays, вызывающий шейдер для генерации луча. Внутри него приложение совершает вызовы функции TraceRay, которая вызывает обход структуры ускорения и выполнение соответствующего шейдера при попадании или промахе (два разных типа шейдеров). TraceRay также можно вызывать изнутри этих двух шейдеров, при использовании рекурсии луча или эффектов с многочисленными отскоками.


Почему бы для трассировки лучей не применять уже известные нам по DirectX вычислительные шейдеры? Во-первых, DXR позволяет запускать отдельные шейдеры при попадании (hit) и промахе (miss) лучей, во-вторых — процесс рендеринга может быть ускорен на графических процессорах (при помощи Nvidia RTX или аналогов у конкурентов), и в-третьих, новый API позволяет привязывать (binding) ресурсы при помощи шейдерных таблиц.

Nvidia RTX — это набор программных и аппаратных алгоритмов, ускоряющих трассировку на решениях компании Nvidia, основанных на графической архитектуре Volta . Почему не поддерживаются предыдущие архитектуры, которые не так уж сильно отличаются от Volta? Возможно, частично это маркетинговый ход, чтобы привлечь покупателей к новым продуктам, а возможно — в Volta есть какие-то аппаратные оптимизации, позволяющие серьезно ускорить трассировку лучей на GPU, о которых нам еще не рассказали. Да, в пока что единственном графическом процессоре с этой архитектурой есть тензорные ядра, ускоряющие задачи искусственного интеллекта, но он если и может применяться при рендеринге с трассировкой лучей, то лишь в процессе шумоподавления, да и то — по имеющимся данным, в существующих алгоритмах шумодавов такие возможности пока что не применяются.

В преимуществах DXR и RTX — мощная и гибкая программная модель, схожая с Nvidia OptiX, позволяющая относительно просто написать эффективные алгоритмы, использующие трассировку лучей. Для начала разработки приложений с использованием трассировки лучей DXR, аппаратно ускоренных при помощи RTX, вам потребуется видеокарта на основе архитектуры Volta (пока это только Titan V ) и драйвер версии 396 или выше, а также операционная система Windows 10 RS4 и пакет для разработчиков Microsoft DXR, содержащий все необходимое. Также для отладки будет полезен Microsoft PIX или NSight Graphics компании Nvidia, которые уже имеют поддержку DXR API.

Для удобства разработки и отладки, Microsoft сразу же выпустила новую версию утилиты PIX for Windows с поддержкой возможностей DXR. Этот инструмент позволяет захватывать и анализировать кадры, построенные при помощи DXR для того, чтобы разработчики понимали, как именно DXR работает с аппаратным обеспечением, отловили все ошибки и оптимизировали свой код. С помощью PIX программисты могут исследовать вызовы API, просматривать состояние объектов и ресурсы, связанные с работой трассировки, а также структуры ускорения. Все это сильно помогает при разработке DXR-приложений.


В итоге, DirectX Raytracing API дополняет возможности разработчиков специализированными шейдерами и структурами, удобными для трассировки лучей, возможностью одновременной работы с остальной частью традиционного графического конвейера и вычислительными шейдерами и т. д. Концептуально это мало отличается от того, что предлагала Imagination Tech еще несколько лет назад в OpenRL и своих аппаратных решениях. Увы, но ImgTec слишком сильно опередила время со своими чипами PowerVR Wizard, но нужно иметь достаточно средств не только на начальные разработки, но и продвижение своего детища. DXR же — это API такой большой и общепризнанной компании, как Microsoft, и оба производителя игровых GPU (Nvidia и AMD, а может, к ним вскоре добавится и Intel, кто знает) уже работают совместно с Microsoft над оптимизацией нового API для их аппаратных архитектур.

Как и у всех закрытых API, у DXR есть и определенный недостаток в том, что разработчик попросту не знает, как работают те или иные вещи внутри API, какие конкретно ускоряющие структуры используются для обеспечения эффективного параллельного рендеринга на графических процессорах, с какими достоинствами и недостатками, какие характеристики (потребление памяти, задержки и т. п.), как работает планировщик трассировки лучей, достигается ли баланс между использованием ПСП, задержками, использованием регистров и т. д., какая часть работы трассировщика выполняется аппаратно на GPU, а какая в драйвере и API. Все подобные решения грешат своей закрытостью, и DXR не является исключением.

К слову, использованию DXR API есть и альтернатива — сотрудники компании Nvidia работают над расширением мультиплатформенного Vulkan API , предназначенным для трассировки лучей — VK_NV_raytracing . Команда разработчиков сотрудничает с коллегами из Khronos для создания мультиплатформенного открытого стандарта, и одной из главных задач является попытка сделать так, чтобы трассировка лучей в DirectX и Vulkan работала максимально похоже.

Игры, использующие растеризацию, зачастую выглядят весьма правдоподобно и реалистично, так как их разработчики потратили много времени на то, чтобы добавить все необходимые эффекты и алгоритмы, имитирующие распространение лучей света в реальности. И в первые годы, возможности DXR будут использовать и для дополнения существующих техник визуализации, вроде отражений в экранном пространстве — для заполнения данных о скрытой геометрии, не видимой в пределах экрана, что приведет к увеличению качества этих эффектов. Но в следующие несколько лет можно ожидать расширение использования DXR для методов, которые не используются при растеризации, вроде полноценного глобального освещения. В дальнейшем, трассировка лучей может и полностью заменить растеризацию при рендеринге 3D-сцен, хотя растеризация еще долго останется носителем идеального соотношения производительности и качества.

На данный момент полноценная аппаратная поддержка DirectX Raytracing есть только у решений Nvidia семейства Volta (при помощи технологии RTX), то есть на сегодня исключительно у дорогущего Titan V, а на предыдущих GPU этой компании, равно как и на графических процессорах AMD, трассировка лучей полностью выполняется при помощи вычислительных шейдеров — то есть доступна лишь базовая поддержка DXR с меньшей производительностью. Впрочем, в AMD уже заявили, что они работают совместно с Microsoft по внедрению аппаратного ускорения трассировки и скоро представят драйвер для его поддержки, хотя пока что складывается впечатление, что существующие архитектуры AMD вряд ли смогут предоставить высокий уровень ускорения аналогично Nvidia Volta. Технология аппаратного ускорения трассировки лучей RTX включает использование еще не анонсированных аппаратных возможностей архитектуры Volta по ускорению трассировки лучей, и игровые решения с ее поддержкой ожидаются ближе к осени этого года.

Если говорить о еще более дальнем будущем, то появление API для ускорения растеризации идет несколько вразрез с общей универсализацией графических процессоров, которые становятся все более похожими на обычные процессоры, предназначенные для вычислений любых типов. Долгие годы идут разговоры о том, чтобы вообще убрать из GPU все блоки, выполняющие фиксированные функции, хотя это пока что не очень хорошо получалось (можно вспомнить не самый удачный Intel Larrabee ). Но в целом более высокая программируемость графических процессоров сделает возможности смешивания растеризации и трассировки еще более простой, да и для полной трассировки никаких API для поддержки аппаратного ускорения может уже не потребоваться. Но это взгляд уж слишком далеко вперед, пока что мы разбираемся с DXR.

DirectX Raytracing и поддержка этого расширения API разработчиками программного и аппаратного обеспечения дают практическую возможность применения трассировки лучей в сочетании с привычным «растеризационным» API. Зачем это нужно, ведь современные GPU и так способны выполнять почти любые вычисления при помощи вычислительных шейдеров и разработчики могут выполнять трассировку лучей с их помощью? Все дело в стандартизации возможностей аппаратного ускорения трассировки на специализированных блоках в составе GPU, которой не будет в случае использования не предназначенных для этого универсальных вычислительных шейдеров. Некоторые новые аппаратные возможности современных графических архитектур позволяют ускорить трассировку лучей и эта функциональность не может быть раскрыта при помощи существующего DirectX 12 API.

Microsoft остается верной себе — как и растеризационная часть DirectX, новый API не определяет то, как именно аппаратное обеспечение должно работать, а позволяет разработчикам GPU ускорять лишь определенные Microsoft стандартизированные возможности. Разработчики же аппаратного обеспечения вольны делать поддержку выполнения команд DXR API так, как им угодно, Microsoft не указывает им как именно должны это делать графические процессоры. Microsoft представляет DXR как вычислительную задачу, которую можно запускать параллельно с «растеризационной» частью, также DXR привносит несколько новых типов шейдеров для обработки лучей, а также оптимизированную структуру для 3D-сцены, удобную для трассировки лучей.

Так как новый API предназначен для разработчиков ПО, Microsoft предоставляет им базовый уровень поддержки трассировки лучей в DXR, который может использовать все существующее аппаратное обеспечение, поддерживающее DirectX 12. И первые опыты с DXR можно начинать на существующих GPU, хоть это и не будет достаточно быстро для применения в реальных приложениях. Все аппаратное обеспечение с поддержкой DirectX 12 будет поддерживать и трассировку лучей и какие-то несложные эффекты можно будет делать даже с расчетом на уже имеющуюся базу видеокарт на руках у игроков. Какие-то эффекты с применением DXR мы увидим в играх уже в этом году, ну а в 2019-м уже совершенно точно — хотя бы в качестве ранней демонстрации возможностей новых технологий.

Вполне вероятно, что в первое время эффективность выполнения трассировки на разных GPU будет сильно отличаться. Решения без нативной поддержки, использующие базовый уровень поддержки через вычислительные шейдеры, будут очень медленны, а GPU с аппаратной поддержкой трассировки ускорят процесс сразу же в несколько раз — как в старые добрые времена начального развития аппаратной поддержки растеризации. Со временем, все большая часть вычислений при трассировке будет выполняться оптимальнее и значительно эффективнее, но для этого потребуются новые графические решения. Первые из которых должны появиться уже в ближайшие месяцы.

Наглядное сравнение растеризации и трассировки

Давайте попробуем посмотреть на конкретных примерах, что может дать трассировка лучей. На самом деле, она используется в играх уже сейчас, но в несколько иных, более примитивных формах. В частности, в алгоритмах с использованием экранного пространства или алгоритме voxel cone tracing при расчете глобального освещения, в том числе в известном алгоритме Voxel Ambient Occlusion (VXAO) компании Nvidia. Но это все же не полноценная трассировка лучей, а скорее хаки с ее использованием в том или ином виде при растеризации, а мы сегодня говорим о полноценной трассировке лучей для всей геометрии сцены.

Современные графические процессоры уже сейчас довольно мощны и способны трассировать лучи света с высокой скоростью при помощи такого ПО, как Arnold (Autodesk), V-Ray (Chaos Group) или Renderman (Pixar) , и многие архитекторы и дизайнеры уже используют аппаратно ускоренную трассировку лучей для быстрого создания фотореалистичных изображений их продукции, что снижает затраты на общий процесс разработки. Вот уже более десятка лет компания Nvidia участвует в разработке аппаратно-ускоренных техник с использованием трассировки лучей в профессиональной сфере, и теперь настал момент для переноса этих возможностей и в игры.

Чтобы помочь игровым разработчикам с внедрением трассировки лучей, Nvidia анонсировала грядущее добавление в GameWorks SDK таких возможностей, как специфические алгоритмы шумоподавления, качественное глобальное затенение, тени от площадных источников света (area lights ) и алгоритм отрисовки качественных отражений.

Самый качественный рендеринг с трассировкой лучей требует большого количества сэмплов (рассчитываемых лучей на пиксель) для достижения высокого качества — от сотен до тысяч! Это зависит от сложности сцены, но даже несколько десятков лучей для расчетов в реальном времени не годятся, так как даже GPU ближайшего будущего с аппаратной поддержкой трассировки смогут обеспечивать приемлемую производительность при гораздо меньшем количестве лучей на пиксель — лишь несколько штук. Есть ли смысл заморачиваться?

Есть, если дополнительно обработать полученное изображение (а ведь мы хотели уйти от хаков растеризации, но похоже, пока что придется мириться с другими). В частности, выполнение трассировки на производительном решении архитектуры Volta позволяет обеспечить производительность реального времени при обсчете 1-2 сэмплов на пиксель с обязательным применением шумоподавления. Уже существующие алгоритмы шумодавов, которые позволяют значительно улучшить качество изображения после трассировки лучей, и это лишь первые разработки, которые продолжаются.

Требования к алгоритмам шумоподавления в реальном времени довольно высоки, нужно уметь обрабатывать очень шумные входные изображения с экстремально низким количеством лучей на пиксель (вплоть от 1 сэмпла), обеспечивать стабильное качество в движении, используя информацию из предыдущих кадров и исполняться крайне быстро, не тратя больше 1 мс времени GPU. Существующие алгоритмы Nvidia позволяют добиться очень неплохих результатов при визуализации отражений, мягких теней и глобального затенения. Для каждого эффекта используются специфические алгоритмы, также использующие информацию о 3D-сцене.


Для рендеринга теней использовалась трассировка лучей с одним сэмплом на пиксель и включенным шумоподавлением


Для расчета глобального затенения (ambient occlusion) использовался обсчет двух лучей на пиксель с шумоподавлением


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

Ray Tracing Denoiser в составе GameWorks SDK — это набор библиотек для использования нескольких техник быстрой трассировки лучей, использующих шумоподавление, весьма важное для трассировки при малом количестве лучей на пиксель, так как результат обычно получается крайне шумный. В состав алгоритмов входит отрисовка мягких теней от площадных источников света и алгоритмы отрисовки отражений и глобального затенения ambient occlusion. Использование шумоподавления позволяет добиться высокой скорости при малом количестве выборок на пиксель, но качество изображения при этом остается отличным — гораздо лучше используемых сейчас техник с имитацией распространения света по сцене и использованием экранного пространства.

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


Тени, полученные при помощи трассировки лучей


Тени, полученные при помощи растеризации и карт теней

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

Для расчета глобального затенения (Ambient Occlusion ) также хотелось бы применять трассировку лучей, так как она обеспечивает значительно более высокое качество, по сравнению со всеми существующими техниками в экранном пространстве (все эти SSAO , HBAO и даже VXAO ). Практически все используемые сейчас алгоритмы просто добавляют темноты по найденным на плоской картинке углам, лишь имитируя распространение света, а применение трассировки позволяет сделать это физически корректным образом.


Глобальное затенение, полученное при помощи трассировки лучей


Глобальное затенение при помощи имитации эффекта с использованием экранного пространства

Более того, все техники, использующие экранное пространство, не учитывают воздействие геометрических объектов вне сцены и за камерой, а также добавляют одинаковое затенение для совершенно разных поверхностей. На показанном выше примере многие из этих проблем отлично видны — заметно, что это всего лишь попытка имитировать распространение света в 3D-сцене, а вот трассировкой достигается заметно более фотореалистичный вид.

При рендеринге отражений трассировка также может дать заметно лучшее качество, по сравнению с применяемыми сейчас методами, использующими экранное пространство, которым недостает данных за пределами экрана (они физически не способны отрисовать в отражении то, что не видно на экране) и которые отрисовывают блики на отражениях некорректно — из-за того, что используется прямое направление взгляда, а не отраженное.


Отражения, полученные при помощи трассировки лучей


Отражения, полученные при растеризации с использованием экранного пространства

Пример Nvidia может и слишком преувеличенный и он чересчур явно показывает проблемы техники отражений, использующей экранное пространство, но смысл понятен — физически корректные отражения можно отрисовать лишь с применением трассировки лучей. Другие методы рендеринга отражений не универсальны и обеспечивают худшее качество — например, отражения на плоскости работают только с плоскими поверхностями. Но и у метода с трассировкой есть минус — при малом количестве выборок потребуется шумоподавление, так как с одним просчитанным лучом на пиксель картинка выходит крайне шумной.

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

Но это лишь начальные версии техник, использующих фильтры шумоподавления, которые будут улучшаться и по качеству, и по производительности. Кроме этого, в будущем возможно применение шумоподавления с использованием технологий искусственного интеллекта, который уже есть в составе Nvidia OptiX 5.0 , но пока что не используется при трассировке при помощи RTX. Вполне вероятно, что в будущем будет использоваться единый шумодав сразу для всех компонент освещения (а не три отдельных, как это делается сейчас) для снижения затрат памяти и производительности. Также ничто не мешает использовать гибридный подход к рендерингу, используя элементы алгоритмов экранного пространства с дополнительной трассировкой лучей.

Кроме применения трассировки лучей в игровых движках реального времени, возможности DXR с аппаратным ускорением на GPU можно использовать и при создании контента. Например, для качественного расчета освещения, которое затем помещается в карты освещения, для создания заранее отрендеренных сцен на игровом движке, но с более высоким качеством и т. д. Более того, можно использовать трассировку лучей вообще не для рендеринга, а в звуковых движках для виртуальной реальности (Nvidia VRWorks Audio ), при физических расчетах или даже в алгоритмах искусственного интеллекта.

Трассировка лучей полезна в процессе создания контента: точной настройке характеристик материалов с качественным и быстрым рендерингом, добавлении и настройке характеристик источников света, отладке алгоритмов шумоподавления и т. п. Также можно сравнительно небольшими силами получить еще более качественный офлайн-рендер, использующий те же структуры и ресурсы, что и движок реального времени. К примеру, это уже сделано в Unreal Engine 4 — сама Nvidia написала экспериментальный Path Tracer сразу после интеграции в движок возможностей DXR, который хоть и не обеспечивает пока что достаточного качества для полноценного офлайн-рендера, но показывает такую возможность.

Мы уж не говорим о возможности быстрой и качественной подготовки карт освещения — «запекания» света в специальные карты освещения (лайтмапы) для статических объектов сцены. Такой движок может использовать один тот же код в игре и редакторе и обеспечивать подготовку различных видов карт освещения (2D, 3D) и кубических карт окружения.


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

Напоследок предлагаем посмотреть все преимущества трассировки лучей в динамике. Nvidia выпустила целую коллекцию технологических демонстраций, показывающих преимущества аппаратно-ускоренной трассировки лучей с применением технологии Nvidia RTX при помощи DXR API (только в виде ролика на Youtube, увы).

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

Демонстрация возможностей трассировки лучей

Чтобы показать возможности DirectX Raytracing API и технологии Nvidia RTX, сразу несколько ведущих разработчиков игровых движков и бенчмарков выпустили к GDC 2018 свои технологические демонстрации, показывающие некоторые возможности новых технологий с применением трассировки лучей: 4A Games, Electronic Arts, Epic Games, Remedy Entertainment, Unity и другие. Увы, пока что они доступны лишь в виде скриншотов, презентаций и роликов на Youtube.

Если ранее подобные демонстрации трассировки лучей в реальном времени показывали или в очень простых сценах с простыми эффектами или при низкой производительности, то возможности будущих GPU способны сделать трассировку лучей реальной даже в игровых условиях с приемлемой производительностью. Разработчики из Epic Games и Remedy Entertainment считают, что возможности DXR и RTX принесут более качественную графику в игры будущего, а внедрение базовой поддержки нового API в их движки оказалась относительно несложным делом.

DirectX Raytracing tech demo (Futuremark)

К примеру, известная всем энтузиастам 3D-графики по своим тестовым пакетам компания Futuremark показала технологическую демонстрацию DXR, сделанную на основе специально разработанного гибридного движка с применением трассировки лучей для качественных отражений в реальном времени.

Мы уже говорили, что при использовании распространенных сейчас методов, отрисовка реалистичных и физически корректных отражений в 3D-сцене весьма непроста, в процессе создания алгоритмов разработчики сталкиваются с кучей трудностей, которые обходятся в итоге разными методами, но от идеала отражения остаются далеки. В последние несколько месяцев разработчики из Futuremark исследовали возможность использования DXR при гибридном рендеринге и достигли весьма неплохих результатов.

При помощи аппаратно ускоренной на GPU трассировки лучей они получили физически корректные отражения для всех объектов сцены, включая динамические. Открывайте следующие несколько картинок в полном размере, так как это GIF-анимации, наглядно показывающие разницу между трассировкой и более привычными методами с использованием экранного пространства:

Разница налицо. Помимо отличий в детализации отражения, при помощи трассировки в DXR можно получить отражения объектов, существующих вне экранного пространства, т. е. не входящих в пределы обзора игровой камеры, как это видно на сравнительных скриншотах, да и само отражение в целом выглядит куда правдоподобнее. Вот еще один пример, может и менее явный, но вполне дающий представление:

Использование трассировки лучей дает точные отражения с коррекцией перспективы на всех поверхностях сцены в реальном времени. Хорошо видно, что трассировка куда ближе к реализму, чем более привычные для нас screen-space отражения, применяемые в большинстве современных игр. Вот еще одно сравнение:

Если не смотреть на отражения, полученные с применением DXR, то и обычные методы могут показаться дающими неплохое качество, но лишь показаться. Более того — отражения важны не только для зеркал с большим коэффициентом отражения, но и для всех других поверхностей — все они становятся более реалистичными даже если это не видно сходу.

В своей демо-программе Futuremark применяет возможности трассировки лучей лишь для решения тех проблем, с которыми трудно бороться обычными методами, вроде отражений динамических объектов, находящихся вне основного экранного пространства, отражений на неплоских поверхностях и отражениях с коррекцией перспективы для объектов сложной формы. Вот более качественные скриншоты из демонстрации возможностей DXR:




На современных GPU уже можно применять гибридный рендеринг с использованием растеризации для большей части работы и сравнительно небольшим вкладом трассировки для улучшения качества теней, отражений и других эффектов, с которыми сложно справиться при помощи традиционных техник с растеризацией. И демо-программа Futuremark как раз и показывает пример подобного подхода, она работает в реальном времени уже на существующем GPU, пусть и одном из самых мощных.

Главное, что по словам разработчиков из Futuremark, им было довольно легко внедрить поддержку трассировки лучей в существующий DirectX 12 движок из бенчмарка 3DMark Time Spy , используя модели и текстуры из своих тестов. Вместе с технодемкой, известные разработчики 3D-тестов анонсировали применение возможностей DirectX Raytracing в их следующем бенчмарке 3DMark, который планируется выпустить ближе к концу текущего года.

Reflections Real-Time Ray Tracing Demo (Epic Games)

Компания Epic Games совместно с ILMxLAB и Nvidia также показала свой вариант включения возможностей по трассировке лучей в реальном времени в движок Unreal Engine 4 . Показ состоялся на открытии GDC 2018, где три указанные компании презентовали экспериментальную кинореалистичную демонстрацию на тематику киносериала «Star Wars» с использованием персонажей из серий «The Force Awakens» и «The Last Jedi» .


Демонстрационная программа Epic Games использует модифицированную версию Unreal Engine 4 и технологию Nvidia RTX, возможности которой раскрываются через DirectX Raytracing API. Для построения 3D-сцены разработчики использовали реальные ресурсы из фильмов Star Wars: The Last Jedi с Captain Phasma в блестящих доспехах и двумя штурмовиками со сценой в лифте корабля First Order .

Рассматриваемая технодемка отличается динамически изменяющимся освещением, которое можно регулировать в процессе, а также эффектами, полученными при помощи трассировки лучей, включая качественные мягкие тени и фотореалистичные отражения — все это отрисовывается в реальном времени и с очень высоким качеством. Подобное качество картинки попросту недоступно без использования трассировки лучей, а теперь его может обеспечить и привычный движок Unreal Engine, чем был очень впечатлен основатель и президент компании Epic Games Тим Свини .

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


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

Кроме отражений можно отметить мягчайшие тени, которые не бросаются в глаза своими рваными и/или очень резкими краями, как это бывает при использовании карт теней. Ну и постобработка тут весьма качественная. В общем, разработчики постарались на славу и эта демонстрация получилась, пожалуй, одной из наиболее впечатляющих для аппаратного ускорения трассировки лучей.

При создании этой демки, Epic Games тесно работали с художниками из ILMxLAB и инженерами из Nvidia для показа возможностей технологии Nvidia RTX, работающей через DXR API. Демка на Unreal Engine работает в реальном времени на рабочей станции DGX Station компании Nvidia, включающей аж четыре графических процессора архитектуры Volta. Объединение возможностей движка Unreal Engine, графического API для трассировки лучей DXR и технологии Nvidia RTX, работающей на графических процессорах семейства Volta, позволило приблизиться к кинореализму в реальном времени.

Кроме технологической демонстрации, на GDC специалисты из Epic Games провели большую часовую сессию «Cinematic Lighting in Unreal Engine» , посвященную новым возможностям их движка. А сама демка показана всем желающим с возможностью посмотреть сцену в различных режимах, включая wireframe-рендеринг. Можно предположить, что все это рано или поздно будет доступно в играх, ведь движок Unreal Engine весьма популярен. Epic Games обещала дать доступ к возможностям DXR API уже в этом году — вероятно, ближе к осени, когда выйдут новые GPU Nvidia.


Поддержка DirectX Raytracing и Nvidia RTX открывает для Unreal Engine 4 путь к новому классу техник и алгоритмов, которые не были доступны ранее при засилье растеризации. В скором будущем, разработчики игр смогут использовать гибридный подход с частичным использованием качественной трассировки лучей для некоторых эффектов и высокопроизводительной растеризацией для большей части работы. Это неплохой задел на будущее, ведь возможности GPU, связанные с эффективным ускорением трассировки лучей, будут только расти.

Pica Pica — Real-time Raytracing Experiment (Electronic Arts/SEED)

Очередным разработчиком, заинтересовавшимся трассировкой лучей через DXR, стала студия SEED из Electronic Arts , которая создала специальную демо-программу Pica Pica , применяющую экспериментальный движок Halcyon , использующий гибридный рендеринг, как и предыдущие демонстрационные программы. Также эта демка интересна тем, что в нем был создан процедурный мир без каких-либо предварительных расчетов.

Почему исследователи из SEED решили использовать гибридный рендеринг с трассировкой лучей? Опытным путем они установили, что такой метод способен дать куда более реалистичное изображение, по сравнению с растеризацией, весьма близкое к полноценной трассировке лучей (path tracing), которая сверхтребовательна к ресурсам или дает слишком шумную картинку при малом количестве просчитанных выборок. Все это отлично видно по сравнительным скриншотам:


Полноценная трассировка


Гибридный рендеринг


Растеризация

В современных играх для расчета отражений и освещения используют различные хаки, в том числе и предварительный расчет освещения (его статической части, по крайней мере). Все это требует дополнительной работы от дизайнеров уровней, хитрым образом расставляющих фейковые источники света, запускающих предпросчет освещения, которое затем записывается в лайтмапы. А использование трассировки лучей в части задач при рендеринге дает возможность отказаться от этой дополнительной работы, ведь трассировка лучей позволяет естественным образом рассчитать все необходимое, как мы уже рассказывали выше.

И так как полноценная трассировка пока невозможна, в движке Halcyon применяется гибридный подход. Для расчета отложенного затенения используется растеризация, для расчета прямых теней можно использовать или растеризацию или трассировку лучей при необходимости, для прямого освещения используются вычислительные шейдеры, для отражений также можно использовать как традиционный подход, так и трассировку, для глобального освещения всегда используется трассировка, а для имитации глобального затенения (ambient occlusion) можно или положиться на обычные экранные методы типа SSAO или также включить трассировку лучей. Для рендеринга прозрачных объектов используется только трассировка, а для постобработки — вычислительные шейдеры.


В частности, трассировка лучей используется при расчете теней и отражений — куда более качественных и естественных, чем при распространенных сейчас техниках. Например, вот такие отражения в общем виде невозможно сделать при алгоритмах расчета отражений при растеризации и использовании экранного пространства:


Трассировка лучей при расчете отражений происходит в половинном разрешении, то есть используется 0,25 луча/пиксель для отражений и 0,25 луча/пиксель для расчета теней. И тут проявляется проблема малого количества просчитанных лучей в виде крайне шумной картинки с отражениями, когда без специальной дополнительной обработки результат трассировки лучей выглядит слишком грубо:


Поэтому после трассировки картинка реконструируется в полное разрешение рендеринга специальным образом — несколькими очень хитрыми алгоритмами (с подробностями можно ознакомиться в выступлении команды разработчиков на GDC 2018), когда полученные данные фильтруются и дополнительно собирается и учитывается информация из предыдущих кадров. В итоге получается вполне приемлемый результат с реалистичными отражениями, мало отличающийся от полноценного path tracing:


Но может быть привычные методы в экранном пространстве дадут не худший результат и «дорогая» нам трассировка просто не нужна? Посмотрите сами на наглядное сравнение: слева показаны отражения в экранном пространстве, посередине — гибридная трассировка лучей, а справа — референсный рендер с полной трассировкой лучей:


Разница очевидна. Метод экранного пространства очень примерный, нереалистичный и лишь имитирует отражения, хотя местами и неплохо, но с явными артефактами и проблемами недостатка разрешения. При трассировке такой проблемы нет, даже с учетом сниженного разрешения при просчете лучей. В Pica Pica трассировка лучей также используется и для отрисовки прозрачных и полупрозрачных объектов. В демо-программе рассчитывается преломление света без необходимости предварительной сортировки, а также подповерхностное рассеивание света:

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


Глобальное освещение выключено


Глобальное освещение включено

Глобальное освещение значительно влияет на некоторые объекты в сцене, добавляя реалистичности их освещению. В демке можно дополнительно использовать также и техники имитации глобального затенения, придающие дополнительные тени. Поддерживаются в том числе и алгоритмы в экранном пространстве — Screen Space Ambient Occlusion (SSAO):


Возможно, еще лучше бы получилось с чем-то вроде VXAO, который продвигает Nvidia, но и так выглядит уже совсем неплохо. Но еще лучше и реалистичнее всего изображение будет с полноценным расчетом глобального затенения при помощи трассировки лучей. Посмотрите на сравнительных картинках, разница бросается в глаза:



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

Что касается теней от прямых лучей источников освещения, то с жесткими тенями при трассировке все совсем просто — запускаются лучи в направлении источников света и проверяются попадания. Для мягких теней алгоритм схожий, но результат при одной выборке на пиксель получается слишком «шумным» и его приходится дополнительно фильтровать, после чего картинка становится более реалистичной:


Жесткие тени, мягкие нефильтрованные и мягкие фильтрованные тени

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

На данный момент в демо-программе Pica Pica вычисляется лишь 2,25 лучей на каждый пиксель (всего, включая все эффекты), и в результате получается фотореалистичная картинка с качеством, близким к полноценной трассировке, хоть и с некоторыми ограничениями. А теперь — ложка дегтя: как и в случае демки Epic Games, для ускорения процесса рендеринга пока что приходится использовать возможности сразу нескольких топовых GPU одновременно и с передачей минимального объема данных по относительно медленной шине PCI Express. Но дальнейшее развитие аппаратного ускорения на графических процессорах должно помочь избавить нас от подобных системных требований в будущем.

Experiments with DirectX Raytracing in Northlight (Remedy Entertainment)

Очередной демонстрационной программой для продвижения DXR и RTX, представленной на GDC 2018, стали эксперименты с игровым движком Northlight Engine финской компании Remedy Entertainment , известной публике по таким играм, как Max Payne, Alan Wake и Quantum Break . Движок Northlight Engine интенсивно разрабатывается компанией, известной своим интересом к последним графическим технологиям. Поэтому немудрено, что они заинтересовались и аппаратно-ускоренной трассировкой лучей.

На GDC компания показала разработки, над которыми они работали с Nvidia и Microsoft. В числе нескольких разработчиков, Remedy получила ранний доступ к возможностям Nvidia RTX и DXR API, которые воплотились в специальной версии движка Northlight. Главный графический программист компании Tatu Aalto представил на конференции выступление «Experiments with DirectX Raytracing in Remedy’s Northlight Engine» , в котором рассказал об особенностях принятого ими гибридного подхода.


В этой демо-программе традиционно используется растеризация для скорости и трассировка лучей для части эффектов, которые сложно сделать иначе. В числе улучшений качества можно назвать физически корректные мягкие тени, качественные глобальное затенение и освещение, а также реалистичные отражения. В видеоролике показан результат работы Northlight Engine со всеми включенными эффектами, рассчитываемыми при помощи трассировки лучей с увеличенным числом просчитанных лучей:

Для проведения опытов по внедрению трассировки лучей, в Remedy создали новую сцену, никак не связанную с играми компании. Как мы уже говорили, DXR API поддерживает два уровня структур ускорения: нижний и верхний. Идея заключается в том, что структура нижнего уровня предназначена для хранения геометрии, а верхний уровень содержит структуры нижнего уровня. То есть каждая полигональная сетка — это одна структура нижнего уровня, а каждый верхний уровень содержит несколько структур нижнего уровня с возможными геометрическими преобразованиями (повороты и т. п.).


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

Работа с динамически изменяемой геометрией делается несколько сложнее, так как построитель нижнего уровня принимает только статические буферы. Но деформация тут также возможна — при помощи вычислительного шейдера, принимающего геометрию и skinning-матрицы и записывающего уже измененную геометрию. Далее можно начинать работу по просчету лучей.

Для начала возьмем ambient occlusion — основанный на определении видимости алгоритм, который можно легко выполнить с применением трассировки лучей. Следующее изображение получено при помощи просчета четырех лучей на пиксель, максимальная длина которых установлена на четыре метра, и результат точно выглядит лучше, чем при методе SSAO, использующем лишь экранное пространство.


В левой половине указан традиционный метод расчета глобального затенения, а с трассировкой лучей — справа. Хотя техника SSAO прилично справляется с некоторыми гранями, ей явно не хватает геометрической информации о сцене — такие алгоритмы не знают, что находится вне экрана или за поверхностями, видимыми камерой. Поэтому и получается явно неидеальный результат, хоть он и явно лучше, чем вовсе без затенения.

Увы, производительность трассировки лучей относительно низка и она обходится гораздо «дороже» методов в экранном пространстве. По данным Remedy, в их демо-программе просчет одного луча на пиксель для глобального затенения с максимальной длиной в 4 метра при разрешении Full HD занимает примерно 5 мс и производительность масштабируется почти линейно, так что просчет 16 лучей будет занимать уже под 80 мс. При постоянном улучшении качества, конечно:


Эти скриншоты сняты при обычном полноэкранном сглаживании с учетом временной компоненты (данных из предыдущих кадров), и без хитрой фильтрации, как это делается в большинстве других демок, показанных на GDC. С хитрым шумоподавлением можно обеспечить приемлемое качество при 1-2 лучах на пиксель.

Кроме глобального затенения, в демке Remedy трассировка лучей используется и для рендеринга обычных теней, которые при растеризации сейчас чаще всего используют каскадные карты теней (cascaded shadow maps — CSM ). Разработчики отмечают, что если движок заполняет тени от направленных источников освещения до просчета освещения, то очень легко будет заменить шейдер каскадных карт теней кодом с применением трассировки, которая запишет рассчитанные данные в тот же самый буфер.


При этом разница в качестве будет явно в пользу трассировки (показана справа). Изображение с трассировкой лучей использует просчет 8 лучей на пиксель без дополнительной фильтрации, а техника CSM использует 16 Percentage Closer Filtering (PCM) выборок со специальным фильтром, применяемым к буферу. Правда, нужно учитывать то, что разработчики явно не оптимизировали работу CSM в этом случае, ведь можно было отрегулировать разрешение карт теней и их фильтрацию, чтобы получить более качественные тени, а это просто тень при настройках их движка по умолчанию.

Но даже с учетом этой скидки, разница налицо — с трассировкой лучей тени получаются куда реалистичнее, они имеют плавные края без зубцов, более качественное размытие по краям и даже мелкие детали (ножки стульев) отбрасывают физически корректную тень. В итоге получаются приличные тени с мягкими и жесткими краями теней ровно там, где это должно быть. Также можно легко рисовать тени от площадных источников света, что крайне трудно делать при растеризации.

Что касается производительности, то в этой демо-программе просчет одного луча для разрешения Full HD составляет менее 4 мс, что несколько быстрее, чем для глобального затенения, даже при том, что лучи тут длиннее. Чтобы внедрить трассировку лучей в имеющийся DX12-движок для рендеринга теней, придется потратить несколько дней работы программистов, но результат стоит того, если производительности в итоге будет достаточно.

Похоже, что Remedy добавили в свой движок чуть ли все эффекты, возможные при трассировке на начальной стадии развития DXR. В том числе отражения, отрисовываемые при помощи трассировки лучей. При этом, тут нет такого очевидного использования в виде чисто зеркальных поверхностей, а скорее более тонкий подход с отражениями на всех объектах, но менее явными. На следующем скриншоте показано сравнение техник с использованием трассировки лучей (справа) и экранным пространством (слева):


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

В движке Northlight Engine уже используется расчет глобального освещения (GI ) — в частности, в игре Quantum Break, и этот эффект включен в движке по умолчанию. Для расчета GI используются воксели с размером примерно в 25 см, которые комбинируются с результатом работы техники глобального затенения SSAO, использующей экранное пространство. В качестве эксперимента, в Remedy заменили SSAO аналогичным эффектом с использованием трассировки лучей и результат стал лучше.


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


А зачем вообще нужно рассчитывать глобальное освещение/затенение, и можно ли обойтись без этого крайне ресурсоемкого шага? Посмотрите на наглядном примере, как выглядит результат расчета одного только прямого освещения:


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


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


Отражения и затенение в сцене выглядят весьма реалистично, на наш взгляд. В частности, лампа отражает все объекты, в том числе невидимое для основной камеры яркое окно. А кружка справа отражает свою собственную ручку — так невозможно сделать при помощи растеризации без хитрых хаков. Единственной явной проблемой трассировки здесь является сильный пиксельный шум, который в Remedy пока что особо не старались убрать. А ведь тот же алгоритм из Nvidia GameWorks мог бы изрядно помочь, не говоря уже о шумодаве с использованием искусственного интеллекта.

Конечно, очень хорошо было бы использовать трассировку лучей везде, где это возможно, но оптимальным решением при гибридном рендеринге является оптимизация с помощью карт теней, которые используются в демке Remedy для большинства источников света, кроме солнца. И так поначалу будут делать в каждом приложении с применением трассировки лучей, потому что прямое ее использование везде обойдется слишком дорого и это пока что невозможно в реальном времени, даже с использованием сразу нескольких GPU.

Важно, что интеграция поддержки DXR и RTX в движок Northlight прошла довольно быстро и безболезненно. Финские разработчики были поражены тем, как быстро у них получилось сделать прототипы улучшенного освещения, затенения и отражений при помощи трассировки лучей — все это с куда лучшим качеством, по сравнению с привычными хаками растеризации. Хотя на данный момент показанные технологии находятся на стадии ранней разработки и далеки от того, чтобы быть включенными в игры прямо сейчас, это отличное начало для их будущего внедрения.

Real-Time Ray Tracing in Metro Exodus (4A Games)

Вполне вероятно, что в ближайшие годы мы увидим не одну игру, использующую гибридный рендеринг с трассировкой лучей для отрисовки части эффектов. В частности, первой (ну или одной из первых, как минимум) должна стать игра Metro Exodus , в которой трассировка лучей через DXR с применением технологии Nvidia RTX будет использоваться для расчета глобального освещения и затенения.

Предполагается, что такой метод расчета GI будет доступен в игре в виде альтернативы более привычным алгоритмам SSAO и IBL (image based lighting, освещение на основе текстуры окружения). Конечно, пока что это крайне ограниченное использование трассировки, но качество глобального освещения/затенения с трассировкой лучей гораздо выше, чем даже у VXAO , не говоря уже о SSAO . Вот наглядное видеосравнение методов экранного пространства с трассировкой, снятое нашими немецкими коллегами с экрана выставочной системы (поэтому заранее извиняемся за качество):

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

Вопрос тут только один — если в ролике показана статическая сцена, вообще без динамических объектов и их влияния на глобальное освещение, то что мешает предварительно обсчитать все в офлайне и занести эти данные в статические карты освещения? Нам кажется, что при динамическом расчете глобального освещения в реальном времени, сцену для демонстрации возможностей нужно было выбрать как-то поживее, хотя бы с перемещаемыми источниками света, не говоря уже о движущихся объектах. Иначе остается удивляться, почему игроки вообще не поняли, что конкретно им показали и почему так невозможно сделать с применением растеризации прямо сейчас.

Выводы

Трассировка лучей обеспечивает куда лучшее качество картинки, по сравнению с растеризацией, и давно используется там, где это возможно — в киноиндустрии, рекламе, дизайне и т. д. Но длительное время она просто не подходила для рендеринга реального времени из-за своей огромной ресурсоемкости — ведь для каждого пикселя необходимо просчитать по несколько лучей, отражающихся от объектов в сцене и преломляющихся в них. При офлайн-рендеринге, не требующем быстрых результатов, этот подход всегда был максимально качественным, а в графике реального времени нам приходилось довольствоваться растеризацией — самым простым и быстрым образом проецирующим 3D-сцену на 2D-экран. Естественно, высокая производительность растеризации имеет недостаток в виде лишь примерных расчетов цвета пикселей в сцене, не учитывающих многие факторы: отражения лучей света, некоторых свойств материалов и т. п. Растеризация даже с кучей хитрых хаков лишь примерно воспроизводит сцену, и любые самые сложные пиксельные и вычислительные шейдеры не дадут качества полноценной трассировки лучей, просто исходя из принципа их работы.

Анонс DXR API и технологии Nvidia RTX дал возможность разработчикам начать исследования алгоритмов, использующих высокопроизводительную трассировку лучей — пожалуй, это самое значительное изменение в графике реального времени с тех пор, когда были представлены программируемые шейдеры. Заинтересованные разработчики уже сейчас показали публике весьма впечатляющие технологические демонстрации, использующие лишь малое количество сэмплов на пиксель при трассировке, и будущее игр в их руках. И руках производителей GPU, которые должны выпустить новые решения, аппаратно поддерживающие трассировку, ожидаемую в нескольких игровых проектах конца этого и начала следующего года.

Естественно, что первые попытки использования трассировки будут гибридными и серьезно ограниченными по количеству и качеству эффектов, а полноценной трассировки придется ждать еще не один десяток лет. Во всех показанных демо-программах используется 1-2 луча на пиксель, а то и менее, в то время как в профессиональных приложениях их сотни! И чтобы получить качество офлайновых рендеров в реальном времени, нужно еще очень долго ждать. Но уже сейчас настало время для начала разработок по внедрению трассировки в существующие движки, и кто будет первым в освоении возможностей DXR, тот может получить определенное преимущество в будущем. Кроме того, трассировка лучей способна облегчить разработку виртуальных миров, так как она избавит от множества мелких задач по ручной доработке теней, лайтмапов и отражений, чем приходится заниматься при неидеальных алгоритмах растеризации. Уже сейчас аппаратно-ускоренную трассировку можно использовать в самом процессе разработки — для ускорения таких вещей, как предварительный просчет лайтмапов, отражений и статических карт теней.

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

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

А ведь с физически корректными глобальным освещением, затенением и отражениями, рассчитанными при помощи трассировки лучей, отрисованная картинка становится реалистичнее даже без наличия эффектных зеркал и других явно отражающих поверхностей. В современных играх почти всегда используют физически корректный рендеринг, в котором материалы имеют свойства шероховатости и отражающих способностей, а также и кубические карты среды, поэтому отражения всегда в наличии, даже если их не видно невооруженным взглядом. В такой игре можно довольно быстро заменить кубические карты среды трассировкой, предложив такую возможность обладателям высокопроизводительных систем. Тени с трассировкой также выглядят лучше и позволяют решить принципиальные проблемы карт теней, хотя некоторые из них и решены в хитрых продвинутых алгоритмах, вроде Nvidia Hybrid Frustum Traced Shadows (HFTS) , также использующих трассировку в определенном виде, но все равно лучше всего будет унифицированный подход. А отрисовка очень мягких теней от площадных источников света способна дать идеальные сверхреалистичные тени в большинстве случаев.

Основная сложность трассировки в том, что не все первые реализации сразу будут смотреться заметно лучше хитрых screen-space методов, но можно сказать точно, что это именно то направление, в котором и нужно двигаться для получения фотореализма. Потому что у алгоритмов в экранном пространстве есть фундаментальные ограничения, через которые не перепрыгнуть. По многим параметрам картинка даже существующих демонстрационных программ весьма неплоха, пусть она и отрисовывается несколькими мощнейшими GPU и использует хитрое шумоподавление. Пока что приходится использовать малое количество лучей на пиксель и давить шум, но в будущем это решится при помощи примитивного экстенсивного развития. Это ведь лишь самые первые пробы с трассировкой лучей в реальном времени, в дальнейшем качество картинки возрастет вместе с производительностью.

Пока что в следующие пару лет мы получим возможность включения одной-двух новых техник, использующих трассировку лучей в дополнение к растеризации или на замену лишь части ее работы. Так делается всегда в начале жизненного пути новых технологий, когда есть возможность отключить новые алгоритмы, слишком тяжелые для среднего игрового ПК. Но если ориентироваться только на них, то никакого прогресса просто не будет. А поддержка аппаратной трассировки со стороны Nvidia важна тем, что они умеют помогать разработчикам внедрять новые технологии. И есть уверенность в том, что Metro Exodus — далеко не единственная игра, в которой Nvidia продвигает трассировку, ведь они сотрудничают с игровыми разработчиками сразу по нескольким проектам. Небезызвестный Тим Свини из Epic Games спрогнозировал, что года через два года GPU обретут достаточную производительность уже для массового использования трассировки лучей в играх и в это можно поверить.

Самые близкие к Microsoft разработчики начали изучение возможностей DXR почти год назад и это лишь начало пути нового API. Тем более, что сейчас на рынке просто еще нет доступных графических решений с поддержкой аппаратного ускорения трассировки. Анонс DXR предназначен для того, чтобы разработчики аппаратного и программного обеспечения начали работу по изучению и оптимизации трассировки лучей и начали раннюю стадию внедрения новых технологий в игры. Заинтересованные разработчики уже начали опыты с DXR и современными GPU, и такие компании, как Epic Games, Futuremark, DICE, Unity и Electronic Arts, даже анонсировали планы по использованию возможностей DXR в будущих версиях игровых движков и игр.

Вполне вероятно, что энтузиастам придется подождать (такова наша доля) появления доступных GPU с аппаратным ускорением, чтобы увидеть даже самые первые эффекты с применением трассировки лучей, так как базовый уровень поддержки через вычислительные шейдеры может оказаться слишком медленным даже для простых алгоритмов. Игры с толковым применением DXR потребуют аппаратной поддержки трассировки, которая будет поначалу только у Nvidia Volta, но грозит активно улучшаться со временем. Возможно также и появление сравнительно простых игр со стилизованной графикой, которые будут использовать исключительно трассировку лучей.

Еще один важный момент заключается в том, что текущее поколение игровых консолей не имеет поддержки аппаратного ускорения трассировки лучей, компания Microsoft ничего не сказала по поводу DXR в Xbox One. Скорее всего, такой поддержки просто не будет, что может стать еще одним тормозом для активного использования возможностей трассировки лучей в играх. Хотя Xbox One имеет почти полноценную поддержку DirectX 12, никаких аппаратных блоков для ускорения трассировки в нем нет, поэтому есть немалая вероятность того, что как минимум до следующего поколения консолей дело ограничится парой-тройкой эффектов с трассировкой лучей в нескольких игровых проектах, поддерживаемых компанией Nvidia, продвигающей свою технологию RTX. Очень хотелось бы ошибиться, ведь энтузиасты компьютерной графики уже заждались подобных глобальных улучшений в рендеринге реального времени.

ВВЕДЕНИЕ

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

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

Окружающие нас объекты обладают по отношению к свету такими свойствами:

излучают;

отражают и поглощают;

пропускают сквозь себя.

Каждое из этих свойств можно описать некоторым набором характеристик.

Излучение можно охарактеризовать интенсивностью и спектром.

Свойство отражения (поглощения) можно описать характеристиками диффузного рассеивания и зеркального отражения. Прозрачность можно описать ослаблением интенсивности и преломлением.

Из точек поверхности (объема) излучающих объектов исходят лучи света. Можно назвать такие лучи первичными - они освещают все остальное. От источников излучения исходит по различным направлениям бесчисленное множество первичных лучей. Некоторые лучи уходят в свободное пространство, а некоторые попадают на другие объекты.

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

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

Непосредственная реализация данной лучевой модели формирования изображения представляется затруднительной. Можно попробовать построить алгоритм построения изображения указанным способом. В таком алгоритме необходимо предусмотреть перебор всех первичных лучей и определить те из них, которые попадают в объекты и в камеру. Затем выполнить перебор всех вторичных лучей, и также учесть только те, которые попадают в объекты и в камеру. И так далее. Такой алгоритм называется прямой трассировкой лучей. Главный недостаток этого метода - много лишних операций, связанных с расчетом лучей, которые затем не используются.

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 с.


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

Традиционная технология

Для тех, кто не знаком с теорией 3D графики, я коротко поясню, в чём заключается метод обратной трассировки лучей, и каково его отличие от традиционного метода игровой графики. В традиционном методе визуализации изображения в компьютерных играх сцена или, если угодно, игровой мир, представляется набором треугольников. Для каждого треугольника задаются текстуры и степень освещённости. Далее треугольники скопом заталкиваются в 3D ускоритель и отрисовываются, примерно, как художник чертит на листе бумаги сплошной треугольник. Отличие состоит в использовании буфера глубины. Буфер глубины требуется, что бы не рисовать треугольники, которые закрыты другими объектами сцены. При отрисовке точек нового треугольника проверяется соответствующее значение буфера глубины. В буфере глубины, или ещё его называют Z-буфер, хранится дальность от наблюдателя до уже нарисованного изображения. Если дальность до точки нового треугольника меньше записанного в Z-буфере значения, то эта точка не накрыта точками более близко расположенных треугольников, и её можно рисовать, при этом так же обновляется значение буфера глубины. Этот метод позволяет построить изображение состоящей из треугольников сцены произвольной сложности. Одно из достоинств этого метода состоит в том, что его можно было реализовать - то есть, визуализировать достаточно содержательную игровую сцену в реальном времени и в высоком разрешении - на "древних" процессорах поколения i386, i486.

Различные способы построения изображения могут отличаться скоростью работы, а так же качеством, реалистичностью или красивостью построенного изображения. Естественно, методы, позволяющие нарисовать более реалистичное изображение, требуют и больших вычислительных ресурсов. Мы, конечно, не рассматриваем заведомо плохие методы, которые и работают медленно, и рисуют плохо. На заре развития индустрии компьютерных игр, когда персональные компьютеры были относительно маломощные, естественно, был выбран самый быстрый, не требовательный к вычислительным ресурсам метод отрисовки, выше упомянутый метод Z-буфера.

Однако, трёхмерная сцена состоит не только из одних геометрических деталей, она не мыслима без света, поскольку иначе мы её просто бы не увидели. А метод Z-буфера позволяет нарисовать только геометрию сцены. Что же делать? Точная физическая модель распространения света очень сложна, речь может идти о неких приближениях к естественному освещению. Требуется, чтобы в затенённых местах, куда не попадают прямые световые лучи, было темно, рядом с источниками света - светло. Для создания реалистичного, с точки зрения освещённости, изображения сцены стали использовать заранее просчитанные текстуры, так называемые lightmap, содержащие значения освещения статических объектов сцены. Такая текстура накладывается в месте с обычной текстурой материала и затемняет её в зависимости от положения объекта на сцене, его освещённости. Естественно, при этом требуется полная статичность сцены и источников света, поскольку просчёт этих lightmap происходит крайне долго. Эта технология используется в компьютерных играх уже много лет, и её использование привело к тому, что трёхмерные игры в части графического движка стали отличаться лишь количеством треугольников и текстур на уровне. Как не было динамических источников света и возможности разрушать уровень, так её и нет, поскольку нет динамического расчёта освещённости-затенённости. Если вы передвинете светильник или закроете окно, освещённость сцены никто не изменит, поэтому такой возможности в играх нет. Есть только так называемые fake решения, когда что-то можно сделать в определённом месте, потому что эта возможность заранее предусмотрена и всё заранее рассчитано.

Только недавно стали появляться тени от динамических моделей, всяких монстров, ботов. Мы ещё затронем тему, каким образом эти тени рассчитываются, но часто они выглядят не естественно, поскольку, например, источников света много, а тень идёт только от одного, резкая и не красивая.
Прогресс игровой графики уже много лет связывают исключительно с появлением новых поколений графических ускорителей. Действительно, оказалось очень удобным переложить работу по рисованию треугольников на акселератор. Задача растеризации и текстурирования треугольника лежит в основе игровой графики, поэтому естественно, что эту очень частную и специфическую операцию удалось кардинально ускорить созданием специально оптимизированного железа. Однако, применение ускорителей привело лишь к улучшению качества изображения, качественным режимам наложения текстур, три линейной и анизотропной фильтрации, возможности использовать большие разрешения и полноэкранное сглаживание изображения. В части расчёта освещённости и динамичности сцены до сих пор ничего не поменялось. Отсутствие динамического света делает уровни современной игры скучными. Статичность освещения и сцены постепенно может надоедать. Как будто время остановилось, и играющие бегают в этом остановленном времени. Сейчас при исследовании возможностей новых ускорителей любят рассматривать экран под лупой, выискивая очередное уже незначительное увеличение качества изображения, которое в игре различить крайне трудно.

Метод трассировки лучей


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

В обзорах процессоров часто упоминаются результаты тестирования в пакетах 3D графики, таких как 3DMax, LightWave и другие. Замеряется время отрисовки какой-нибудь сложной сцены с реалистичным освещением, отражением и преломлением света. Вот как раз сцена и рисуется с помощью метода трассировки лучей.

В отличие от метода Z-буфера, метод трассировки лучей изначально рассчитан на построение реалистичного изображения со сложной моделью освещения. Принцип обратной трассировки лучей состоит в том, что через каждую точку экрана как бы проводится обратный луч света до пересечения с ближайшим объектом сцены, далее из этой точки проводится луч в направлении источника света, таким образом, моделируется распространение света. Если луч, выпушенный на источник света, ничего не пересекает на своём пути, значит данная точка освещена, иначе она лежит в тени. Если луч попадает на зеркальную поверхность, то, в соответствии с законами оптики, выпускается отражённый луч, что даёт возможность построить отражение. В зависимости от свойств среды, через которую проходит луч, он может преломляться, что позволяет моделировать сложные реалистичные световые эффекты. Этот метод позволяет получить не только тени от объектов, но и рассчитать вторичное освещение, когда отражённый тусклый свет попадает в непосредственно затенённые области и размывает тени.

Однако, легко понять, что этот метод крайне вычислительно сложен. Можно обратить внимание в тестах процессоров в программах 3D моделирования, как долго считается даже 1 кадр. Никаким реальным временем тут и не пахнет. Тем более, раньше на персональных компьютерах он выполнялся ещё медленнее, что не оставляло возможностей для его применения в компьютерных играх.
Но за последние несколько лет мощность персональных компьютеров серьёзно возросла и позволила осуществлять метод трассировки лучей почти в реальном времени, правда, с большими ограничениями по качеству изображения и разрешению.

Поскольку для каждой точки экрана нужно осуществить очень сложную процедуру трассировки луча, то скорость трассировки очень сильно зависит от разрешения экрана, от его площади. То есть, построение изображения размером 1024x768 будет занимать в 10 раз больше времени, чем отрисовка изображения в разрешении 320x240. Реализовать метод трассировки лучей в реальном времени можно, весь вопрос, в каком разрешении и с каким качеством изображения.


До последнего времени трассировка лучей в реальном времени на PC была уделом небольших демо - программ, рисующих красивые изображения, но работающих с низкой скоростью и в низких разрешениях. Таких программ полным полно на www.scene.org . Однако, мне удалось, временно пожертвовав многими прелестями метода трассировки лучей, создать полноценный 3D-движок и на его основе первую компьютерную игру, использующую трассировку лучей в реальном времени.

Concept game с 3D движком на основе метода обратной трассировки лучей

На разнообразных автомобильных выставках демонстрируются так называемые concept-car, реальные прототипы будущих серийных автомобилей. Они крайне дороги, не отлажены с потребительской точки зрения, но олицетворяют собой новые идеи. Я же создал concept-game. Что же получилось реализовать, что бы работало в реальном времени на современных персональных компьютерах?
Для движка на трассировки лучей было изначально установлено два главных требования: чтобы расчёт всей освещённости сцены происходил в реальном времени, и чтобы не использовалась никакая заранее рассчитанная информация об уровне. Всё это должно позволить произвольно изменять уровень в динамике. То, что не могут обеспечить современные движки.
Вкупе с динамическим расчётом освещения отсутствие предварительной информации позволяет довольно просто рисовать бесконечные миры, поскольку нужно хранить только не очень объёмную информацию о геометрии уровня.

Выполнение этих строгих требований на современных процессорах потребовало введения других серьёзных ограничений, к счастью, не принципиальных. Однако, с ростом доступной вычислительной мощности эти ограничения будут сниматься, а суть - оставаться.
В первую очередь, я отказался от моделирования именно земной реальности в пользу инопланетных миров. Это позволило отказаться от использования не очень удобного для рейтрейсинга треугольника в качестве основного примитива для конструирования сцены. Инопланетный мир не обязан быть угловатым, пусть он будет круглым. В качестве примитива для построения сцены была выбрана сфера. Поскольку современные игры должны работать в высоких разрешениях, таких, как 1024x768, пришлось отказаться от расчёта отражений и преломлений, поскольку это очень сильно усложняло обработку соответствующего точке экрана луча. Но с ростом вычислительной мощности можно будет расширить как множество примитивов, так и глубину трассировки луча, то есть, добавить отражения, преломления и т.п.

Итак, каковы основные характеристики VirtualRay - 3D движка, построенного на методе трассировки лучей? На самых современных процессорах для персональных компьютеров он работает с более-менее приемлемой скоростью в разрешении 1024x768x32. Будем исходить, что используется именно это разрешение, поскольку если использовать меньшее разрешение, то параметры производительности могут быть другими.

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

Покадровый расчёт всей освещённости и затенённости. Все источники света - динамические (даже статические), поскольку они на самом деле динамические, только не изменяющие положение от кадра к кадру.

Попиксельный расчёт освещённости и попиксельное наложение теней, естественно, динамических.

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

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

Поддержка точечных источников света и бесконечно удалённых источников света типа солнца. Как правило, сцену освещает один "солнечный" источник света, и несколько локальных.

Полностью динамическая сцена, то есть, положение объектов может меняться произвольным образом.

Наложение и билинейная фильтрация текстур.

Ограниченное использование прозрачных сфер с динамическим коэффициентом прозрачности.

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

К локальным недостаткам движка в первую очередь можно отнести то, что он скуп на "дешёвые" эффекты, вроде спрайтовых вспышек и т.п., которые так замечательно делают современные видео ускорители.
Какую же игру оказалось возможным создать с использованием движка VirtualRay? Вообще, на нём можно сделать великое множество игр, начиная от космического симулятора и заканчивая многопользовательской онлайновой вселенной. Кстати, в последнем типе особенно проявляются преимущества движка по реализации динамического изменения сцены. В качестве "концептуальной игры" я создал проект под названием AntiPlanet - простенький 3D shooter с прямолинейными монстрами с поведением в духе Doom. Уровни к игре представляют собой разного размера куски инопланетной местности, освещаемой местными солнцами. Кстати, Солнце совершает движение по небу в соответствии с которым меняется освещение и затенение сцены. Всего в текущей версии игры доступно 5 уровней, один из которых - indoor, лабиринт из пещер. Остальные, в основном, открытые. Движок достаточно универсален, чтобы без специальных оптимизаций рисовать как открытые, так и закрытые сцены.

Есть 5 видов охотящихся за играющим монстров, монстры отличаются типом используемого оружия, скоростью и силой. Кстати в распоряжении играющего - десять видов оружия, стреляющего разнообразными снарядами, ракетами и бомбами. Сферическая природа оружия делает его несколько однообразным, зато снаряды при взрыве разлетаются на кучу осколков. Есть 3 основных вида игры - просто охота на монстров, когда игроку требуется за определённое время уничтожить определённое количество монстров. Второй вид игры состоит в нахождении спрятанных на уровне специальных артефактов. И в третьем случае играющему просто предстоит выжить определённое время на неизвестной планете. При выборе игры можно самому установить количество аптечек, оружия и монстров на уровне, они будут расставлены в случайные места. Конечно, если задать очень большое количество монстров, игра будет работать медленно.

К сожалению, игра не раскрывает весь потенциал движка, поскольку мы с моделлером просто не успели это сделать. Например, нет разрушения уровня, только отдельные динамические части, поскольку тогда тупые монстры дороги не найдут, с другой стороны, это не предусмотрено идей игры. Не полностью реализованы возможности движка по части анимации моделей. Движок позволяет произвольное независимое изменение моделей на каждом кадре, что делает возможным реализацию самой изощрённой анимации.
Я решил не приводить ни каких скриншотов из игры, поскольку они совершенно не передают достоинства движка, как-то динамическое освещение и мягкие тени. Скачайте демо-версию, она занимает всего несколько мегабайт. А так представьте себе сюрреалистическую инопланетную местность, состоящую из огромного количества шаров, монстров из небольших сфер, которые при взрыве разлетаются на мелкие кусочки. Скачать текущую демо-версию можно по этой ссылке .

Для игры требуется Windows95 и выше, желательно больше 128 мегабайт памяти, иначе отключите музыку, DirectX, видео карта с поддержкой 32-битного цвета, и, самое главное, процессор помощнее. Например, процессор Intel Pentium 4 с поддержкой технологии Hyper-Threading, или новый AthlonXP. Игра должна запускаться на любом процессоре с технологией MMX, однако для полной функциональности нужна поддержка SSE, то есть, процессор начиная с Pentium-III. Видео ускоритель не требуется. Кстати, движок поддерживает многопроцессорность, в том числе, и технологию Hyper-Threading. Не вся программа использует несколько потоков для успешного использования Hyper-Threading, но главный цикл трассировки лучей распараллелен, и достигается выигрыш в несколько десятков процентов. А на многопроцессорной системе выигрыш пропорционален количеству процессоров.

Развитие движка VirtualRay

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

И сейчас можно реализовать обработку треугольников вместе со сферами. Но чтобы отобразить нечто содержательное с помощью треугольников, их нужно очень много, а обрабатываются они со скоростью сфер. Несложно расширить множество примитивов за счёт введения сфер с вырезами, сферических сегментов и сферических треугольников. Но это тоже негативно скажется на скорости.
Есть различные методы повышения производительности рейтрейсинга за счёт ухудшения качества изображения. Трассируются не все лучи, а только наиболее важные, а для построения недостающей части изображения использования интерполяция. Однако, такой подход применим не ко всем сценам, иногда он может давать более-менее качественные результаты, а иногда сильно портить изображение.

Кстати, о качестве. Тут есть большой резерв для улучшения. Дело в том, что процедура текстурирования выполняется всего один раз на точку и не отнимает много времени. Сейчас на текстурирование тратится около 10% времени рендеренга. Так что, для улучшения качества текстурирования планируется реализовать попиксельную трилинейную фильтрацию, это не должно существенно понизить скорость.

Рейтрейсинг и современные 3D-ускорители

Последнее время индустрия 3D-ускорителей совершает переход к повсеместному использованию так называемых пиксельных и вертексных шейдеров. При растеризации треугольника для каждого фрагмента изображения ускоритель выполняет заранее заданную программку, которая изменяет сложным образом цвет фрагмента. Она может ещё много чего делать, например, какие-то промежуточные вычисления записывать в текстуры, которые потом будут читаться и использоваться при отрисовке чего-то другого. Типичным примером современного пиксельного, или как его ещё называют, fragment шейдера является шейдер, вычисляющий освещение данной точки треугольника. Он устроен следующим образом: берётся вектор - глобальная позиция источника, берётся текущая координата точки треугольника в трёхмерном пространстве, которая вычисляется в чипе ускорителя при растеризации треугольника, и нормаль к треугольнику в данной точке. Далее вычисляется вектор из данной точки в направлении на источник света и в зависимости от угла, который он образовывает с нормальным перпендикулярным вектором, высчитывается освещённость. Чем под большим углом падает свет, тем меньше его интенсивность.
Как мы видим, современный шейдер может быть содержательной геометрической программой. Сейчас принято тестировать новые ускорители путём измерения скорости выполнения таких вот и более сложных шейдеров. Производительность получается очень большой. Шейдер, выполняющий попиксельное освещение работает в разрешении 1024x768 со скоростью 100-200 кадров в секунду на последних акселераторах, таких, как Radeon9700 или GeForceFX. Имеется ввиду только время работы непосредственно шейдера. В связи с этим давно уже появилась мысль использовать такую немалую вычислительную мощность в самых разнообразных целях, даже далёких от 3D графики. И, в том числе, попытаться использовать для реализации метода трассировки лучей.

Однако, если рассмотреть эту мощность с точки зрения количества скалярных и векторных вычислений с плавающей точкой в единицу времени, то она оказывается сравнимой с вычислительной мощностью современных процессоров. Возьмём самый новый на сегодняшний день ускоритель GeForceFX5900Ultra, он имеет частоту 450MHz, 4 пиксельных процессора, каждый из которых может совершать 1 векторную операцию за такт. На самом деле, операций за такт может быть больше, но нам интересны только вычисления с полной точностью float32, поскольку вычисления с меньшей точностью имеют смысл в основном для вычисления цвета, диапазон которого всё равно ограничен не очень большим цветовым разрешением монитора. А для геометрических расчётов требуется хорошая точность. Получается 450Mx4=1800 миллионов векторных операций в секунду как грубая оценка производительности. Если же взять Pentium 4, то при использовании SSE можно достичь одной векторной операции за полтора такта, то есть при частоте 2700MHz получим те же 1800 миллионов векторных операций в секунду. В обоих случаях имеет в виду, естественно, пиковая производительность, когда весь код только и состоит из вычислений.
Видно, что превосходства в вычислительной мощности у VPU нет. Его преимущество в графике заключается в умении параллельно с вычислениями шейдера производить сопутствующие вычисления, необходимые для растеризации треугольника. Как-то вычислять значение буфера глубины, интерполировать по поверхности треугольника заданные в вершинах значения, и осуществлять за такт выборку и фильтрацию текстур. Всё это осуществляется различными параллельно работающими блоками видео ускорителя.

Так что никакого особого преимущества в реализации трассировки лучей от использования видео ускорителя мы, естественно, не получим, поскольку ускоритель полностью оптимизирован и построен с точки зрения оптимизации рисования треугольников.
Относительно оптимизации трассировки лучей с помощью видео ускорителя есть ещё такая идея: нарисовать всю геометрию на VPU, а расчёт освещения методом трассировки лучей выполнить посредством CPU, а затем скомбинировать результат. Но толку от этого особого не будет, потому, что основные вычислительные сложности приходятся как раз на вычисление освещения. Причём, чем сложнее будет сцена и, соответственно, больше выигрыш от использования VPU, тем больше ресурсов потребуется для расчёта освещения сложной сцены, и рисование сцены будет занимать значительно меньше времени относительно времени расчёта освещения.

Расчёт освещения с помощью современных ускорителей

Хорошо, а каким же образом предлагается рассчитывать затенённость сцены в новых играх с динамическими источниками света, такими, как Doom III? Неужели мы теперь навсегда обречены видеть в компьютерных играх заранее рассчитанное статическое освещение? Нет, давно известны интересные методы расчёта теней на основе использования стандартного метода рисования текстурированых треугольников с помощью z-буфера. Известны они давно, но они такие требовательные к вычислительным ресурсам, что их применение в компьютерных играх, и то, ограниченное, стало возможным только недавно с появлением нового поколения видео ускорителей.

Рассмотрим для начала метод, с помощью которого рисуются динамические тени в вышеупомянутой игре Doom III. Игре, которую очень ждут многие геймеры. Этот метод называется методом Теневых объёмов, или методом отрисовки теней с помощью стенсил - буфера. Вот принципиальная схема его работы: сначала рисуется не освещённая сцена, далее для каждого отбрасывающего тень объекта сцены строится его теневой объём. Теневой объём - это фигура, ограничивающая теневую область, ту область пространства, в которую не попадает свет, которая затенена данным объектом. Мы как бы представляем простирающуюся за объектом черноту в виде тела. Теневой объём можно даже в реальности увидеть, если осветить резким светом комнату, в которой летают частички пыли. Не затенённые частицы будут светиться, а затенённые будут образовывать черную область за загораживающим свет объектом. Следующий шаг состоит в отрисовке треугольников, составляющих границу этого теневого объёма. Путём сравнения значения буфера глубины с глубиной передней и задней стенок теневого объёма определяется, лежит ли данная точка в теневом объёме и, таким образом, затеняется, или нет. Вот при сравнении глубины стенок теневого объёма и глубины изображения используется стенсил буфер - отвечающий экранным пикселям массив значений. В нём хранятся промежуточные результаты сравнения глубины стенок теневого объёма с глубиной изображения. Этот метод "хорош" тем, что вовсю использует fillrate ускорителя, поскольку теневые объёмы, как правило, имеют большую площадь на экране, чем отбрасывающий тень объект. Метод был доступен для реализации ещё на ускорителях Riva TNT2, но он такой требовательный, что его применение стало возможным только недавно.

С другой стороны, построение оптимальных теневых объёмов для сложных не выпуклых объектов является непростой с вычислительной точки зрения задачей. Решение "в лоб" приведёт к возникновению большого количества лишних стенок теневого объёма, отрисовка которых потребует дополнительных ресурсов. Время нахождения эффективного объёма очень быстро растёт со степенью детализации модели. Возможно, именно благодаря этому модели монстров в NewDoom менее детализированы, чем ожидалось.
Но это ещё не все недостатки. У многих небольших объектов площадь стенок теневого объёма может достигать гигантской величины. Например, у расчески. Её теневая область не велика, но очень извилиста. Далее, метод не очень хорошо совместим с прозрачными поверхностями. Например, если в теневой объём попадает прозрачная поверхность, тогда находящийся за ней объект не оставляет своей информации в буфере глубины, поскольку эта информация затёрлась глубиной прозрачной поверхности. И определить, лежит ли объект в теневом объёме, невозможно. Все случаи такого рода придётся обрабатывать отдельно, что будет приводить к увеличению количества проходов рендеренга.

Данный метод трудно усовершенствовать для получения размытых теней. Те, кто смотрел предварительную версию Doom III, могли обратить внимание на резкость теней. И, собственно, этот метод годится только для рисования теней, вторичное освещение с его помощью не рассчитаешь, преломление и отражение света тоже. Просто в лоб рисуется теневой конус объекта и всё.

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

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

Я даже не знаю, какой из выше описанных методов более требователен к fillrate ускорителя. Дело в том, что для получения хорошего качества тени, требуется, чтобы теневая текстура была очень большого разрешения. В новых играх, вроде Splinter Cell, используются текстуры размером несколько тысяч пикселей. Причина заключается в том, что при проецировании самые мелкие детали многократно увеличиваются в размере. Становятся видны составляющие изображение пиксели. Таким образом, этот метод можно использовать только для наложения теней на близко расположенные объекты. Вторым недостатком этого метода является невозможность самозатенения объекта, требуется точно выделять отбрасывающий тень объект, и его части не будут отбрасывать тень друг на друга. И в дополнение, естественно, никакого обобщения для расчёта вторичной освещённости, отражений и преломления света, этот метод не предполагает.

И, наконец, рассмотрим самый, на мой взгляд, перспективный для использования в современных играх метод построения теней. Он является развитием предыдущего проективного метода. Только вместо силуэта объекта в теневую текстуру записывается расстояние от точек объекта до источника света. Далее, при проецировании теневой текстуры эта информация используется для определения, лежит ли точка потенциально затеняемого объекта дальше, или ближе к источнику света, чем затенитель. Преимущество этого метода состоит в корректном самозатенении объекта. А недостатки у него аналогичны предыдущему методу. Этот способ построения динамических теней не пользуется популярностью у разработчиков игр. "Вина" метода заключается в том, что он требует специфических возможностей видеокарты, которые впервые появились в GeForce3 - Geforce4, но были изъяты из Geforce4MX - сокращённой версии Geforce4. Без поддержки железа метод реализовать невозможно, так что приходится использовать способ, осуществимый на всех популярных видео картах.

Преимущество всех выше названных методов заключается в хорошей совместимости с существующим "железом". Для них, по сути, ничего не надо, кроме fillrate и простейших операций. В итоге, можно сделать вывод о том, что видео ускорители даже сейчас далеки от расчёта освещения сцены в реальном времени. И ничего революционного не предвидится. Появились тени от некоторых динамических объектов, ограниченный динамический свет в новом Doom III, вот эти технологии будут осваиваться в течение долгого периода времени.

Развитие ускорителей с точки зрения рейтрейсинга

Как я уже упоминал, современные ускорители становятся всё более и более программируемыми и мощность их неуклонно растёт. Производители видеокарт даже используют термин "Визуальный процессор" применительно к новым изделиям. Действительно, по своим возможностям, ускорители всё больше и больше напоминают обычные процессоры для персональных компьютеров. Вот именно с увеличением степени программируемости VPU связываются надежды по реализации интеллектуальных методов построения изображений, таких, как метод трассировки лучей. Что бы ускоритель можно было перепрограммировать подходящим образом.

Оценим перспективы развития ускорителей в этом направлении. Сейчас новейшие ускорители работают на частотах около 500MHz, как процессоры пятилетней давности, и имеют 4-8 параллельно работающих конвейеров. Сейчас большинство шейдерных векторных операций, сложение, скалярное произведение, выполняется за такт. Многие вспомогательные операции, вроде интерполяции значений по поверхности треугольника, тоже выполняются за такт. Вычисление тригонометрических функций, таких, как sin и cos, правда приближённое, выполняется тоже за такт. При этом используются выборки из таблиц с заранее просчитанными значениями, но, всё равно, производительность удивительна. Тем более, странно, что современные CPU для персональных компьютеров ничего подобного не умеют. Наоборот, наблюдается тенденция по избавлению от сложных команд и замене их несколькими простыми. Эти меры требуются для возможности наращивания частоты. Не вдаваясь в технические тонкости, можно сказать, что всё более и более уменьшающийся с ростом частоты процессорный такт требует более коротких команд. Сложные инструкции всё равно расщепляются внутри современных процессоров на микро операции. Это расщепление - тоже отдельная проблема, ей занимаются целые блоки процессора.

А что же видео ускорители? Вероятно, что для увеличения частоты придётся серьёзно переработать архитектуру современных VPU. Но это ещё полбеды. Для истинной программируемости требуется исполнение процессором ветвлений, то есть, команд управления выполнением программы. А с этим - всегда самые большие проблемы. Как современные процессоры страдают от непредсказуемых условных переходах в программах? Вот вершинные шейдеры в GeForceFX получили команды условных переходов, вы можете посмотреть свежие тесты, как сильно "просела" производительность. И это на сравнительно невысокой частоте ниже 500MHz. А с ростом частоты потери от условных переходов только увеличатся, да и сама их реализация - труднее. Кстати, фантастическая производительность акселераторов достигается при исполнении так называемых потоковых операций, когда данные идут сплошной полосой и обрабатываются по жёстко определённой схеме, никаких тебе случайных условных переходов и т.п. Все эти факты говорят о том, что увеличения частоты видео ускорителей ожидать в ближайшее время не приходится.

Важным параметром видеокарты является количество пиксельных процессоров. Они параллельно закрашивают пиксели, поэтому, чем их больше, тем лучше. На самых новых Radeon их аж восемь. От новых ускорителей ожидают всё большего количества fragment процессоров. Но не всё так просто. Дело в том, что когда размеры треугольника соизмеримы с количеством пиксельных конвейеров, они не могут все работать вместе. В маленьком треугольнике им не хватает места. В том числе, по этой причине производители видео ускорителей так любят режимы анти-алиасинга с отрисовкой всей сцены с удвоенным разрешением. Тогда маленькие треугольники становятся больше. Действительно, если сцену из больших треугольников разбить на более мелкие без изменения формы, то производительность выполнения пиксельных шейдеров существенно снизится, хотя общая площадь треугольников останется прежней.

Развитие современных ускорителей игровой графики и так сопряжено с большими трудностями, и идёт практически только за счёт усовершенствования технологического процесса производства видео чипов. Вся NVIDIA и ATI думают о том, как сделать эффективно простые динамические тени. Хорошего решения нет - им не до рейтрейсинга.

Специализированный ускоритель для рейтрейсинга

Если современные игровые VPU изначально проектировались для ускорения стандартного алгоритма рисования треугольников и мало пригодны для реализации трассировки лучей, то, может быть, имеет смысл изначально строить ускоритель для реализации рейтрейсинга? Увы, ускорять трассировку лучей - неблагодарное занятие.


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


Зато у трассировки лучей есть другое достоинство - она хорошо распараллеливается. Каждый луч можно рассчитывать независимо, что позволяет эффективно реализовать алгоритм на мультипроцессорных системах. В качестве дешевого ускорителя трассировки лучей можно рассматривать знаете что? Систему на четырёх Celeron частотой от 3 гигагерц, или четырёх AthlonXP с урезанным кэшем. Алгоритм трассировки лучей при правильной оптимизации не требователен к большому размеру кэша, так что получится дёшево и многофункционально. Совокупная вычислительная мощность будет намного превосходить текущие настольные компьютеры. Но этого не будет, поскольку многопроцессорные системы предназначены для другого рынка, не для домашних систем.

Заключение

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

Ссылки


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 движка.

Метод обратной трассировки

Метод обратной трассировки лучей позволяет значительно сократить перебор световых лучей. Метод разработан в 80-х годах, основополагающими считаются работы Уиттеда и Кея. Согласно этому методу отслеживание лучей производится не от источников света, а в обратном направлении - точки наблюдении. Так учитываются только те лучи, которые вносят вклад в формирование изображения.

Рассмотрим, как можно получить растровое изображение некоторой трёхмерной сцены методом обратной трассировки. Предположим, что плоскость проецирования разбита на множество квадратиков - пикселов. Выберем центральную проекцию с центром схода на некотором расстоянии от плоскости проецирования. Проведем прямую линию из центра схода через середину квадратика (пиксела) плоскости проецирования. Это будет первичный луч обратной трассировки. Если прямая линия этого луча попадает в один или несколько объектов сцены, то выбираем ближайшую точку пересечения. Для определения цвета пиксела изображения нужно учитывать свойства объекта, а также то, какое световое излучение приходится на соответствующую точку объекта.

Если объект зеркальный (хотя бы частично), то строим вторичный луч - луч падения, считая лучом отражения предыдущий, первичный трассируемый луч. Выше мы рассматривали зеркальное отражение и получили формулы для вектора отражённого луча по заданным векторам нормали и луча падения. Но здесь нам известен вектор отражённого луча, а как найти вектор падающего луча? Для этого можно использовать ту же формулу зеркального отражения, но определяя необходимый вектор луча падения как отражённый луч. То есть отражение наоборот, i.

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

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

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

Когда выясняется; что текущий луч обратной трассировки не пересекает какой-либо объект, а уходит в свободное пространство, то на этом трассировка для этого луча заканчивается.

Рис. 14.1 Пример обратной трассировки лучей.

Обратная трассировка лучей в том виде, в котором мы её здесь рассмотрели, хоть и сокращает перебор, но не позволяет избавиться от бесконечности числа анализируемых лучей. В самом деле, данный метод позволяет сразу получить для каждой точки изображения единственный первичный луч обратной трассировки. Однако вторичных лучей отражения уже может быть бесконечное количество. Так, например, если объект может отражать свет от любого другого объекта, и если эти другие объекты имеют достаточно большие размеры, то какие именно точки излучающих объектов нужно учитывать для построения соответствующих лучей, например, при диффузном отражении? Очевидно, все точки.

Принцип работы метода трассировки лучей:

1. Испускается воображаемый луч из глаза наблюдателя через некоторый пиксел экрана и отслеживается его путь, пока он не пересечет объект.

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

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

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

5. Влияние всех лучей, сгенерированных явно или неявно с помощью начального луча, суммируется и результат определяет RGB-значение данной точки.