Предыдущая глава

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

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

Алгоритм трассировки лучей создает изображение, состоящее из пикселей. Для каждого пикселя на изображении он посылает основной луч в сцену. Направление этого первичного луча определяется путем проведения линии от глаза к центру этого пикселя. Как только мы зададим направление этого основного луча, мы проверим каждый объект в сцене, чтобы увидеть, пересекается ли он с каким-либо из них. В некоторых случаях основной луч будет пересекать более одного объекта. Когда это происходит, мы выбираем объект, точка пересечения которого находится ближе всего к глазу. Затем мы направляем теневой луч от точки пересечения к источнику света (рис. 1).

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

Точка попадания подсвечивается, если в этот луч не пересекается с объектом на пути к свету. Если он пересекается с другим объектом, этот объект отбрасывает на него тень (рис. 2).

Рисунок 2: Маленькая сфера отбрасывает тень на большую сферу. Теневой луч пересекает маленькую сферу, прежде чем попасть на свет.
Рисунок 2: Маленькая сфера отбрасывает тень на большую сферу. Теневой луч пересекает маленькую сферу, прежде чем попасть на свет.

Если мы повторим эту операцию для каждого пикселя, то получим двумерное представление нашей трехмерной сцены (рис. 3).

Рисунок 3: Чтобы отрисовать кадр, мы рассчитываем основной луч для каждого пикселя буфера кадров.
Рисунок 3: Чтобы отрисовать кадр, мы рассчитываем основной луч для каждого пикселя буфера кадров.

Вот реализация алгоритма в псевдокоде:

for (int j = 0; j < imageHeight; ++j) { 
    for (int i = 0; i < imageWidth; ++i) { 
        // вычисляем направление основного луча
        Ray primRay; 
        computePrimRay(i, j, &primRay); 
        // выстреливаем лучём в сцену и ищем пересечение
        Point pHit; 
        Normal nHit; 
        float minDist = INFINITY; 
        Object object = NULL; 
        for (int k = 0; k < objects.size(); ++k) { 
            if (Intersect(objects[k], primRay, &pHit, &nHit)) { 
                float distance = Distance(eyePosition, pHit); 
                if (distance < minDistance) { 
                    object = objects[k]; 
                    minDistance = distance;  // обновляем минимальную дистанцию
                } 
            } 
        } 
        if (object != NULL) { 
            // расчитываем освещение
            Ray shadowRay; 
            shadowRay.direction = lightPosition - pHit; 
            bool isShadow = false; 
            for (int k = 0; k < objects.size(); ++k) { 
                if (Intersect(objects[k], shadowRay)) { 
                    isInShadow = true; 
                    break; 
                } 
            } 
        } 
        if (!isInShadow) 
            pixels[i][j] = object->color * light.brightness; 
        else 
            pixels[i][j] = 0; 
    } 
} 

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

Артур Аппель впервые описал эту технику в 1969 году в статье, озаглавленной "Некоторые методы затенения машинных изображений твердых тел". Итак, если этот алгоритм такой замечательный, почему он не заменил все остальные алгоритмы рендеринга? Главной причиной в то время (и даже сегодня в какой-то степени) была скорость. Как упоминает Аппель в своей статье:

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

Другими словами, это медленно (как однажды сказал Каджия - один из самых влиятельных исследователей всей истории компьютерной графики: "трассировка лучей не медленная - это компьютеры медленные"). Поиск пересечения между лучами и геометрией отнимает невероятно много времени. На протяжении десятилетий скорость алгоритма была главным недостатком трассировки лучей. Однако по мере того, как компьютеры становятся быстрее, это становится все меньшей и меньшей проблемой. Хотя все же следует сказать одну вещь: по сравнению с другими методами, такими как алгоритм z-buffer, трассировка лучей все еще намного медленнее. Однако сегодня, благодаря быстрым компьютерам, мы можем вычислить кадр, который раньше занимал один час, за несколько минут или меньше. Отслеживание лучей в реальном времени и в интерактивном режиме - актуальная тема.

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

Комментарии (0)