Участник:Vlamakarenko/Трассировка лучей

Материал из Алговики
Перейти к навигации Перейти к поиску

авторы: В.А.Макаренко, Р.А.Габдуллин

Свойства и структура алгоритма

Общее описание алгоритма

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

Математическое описание алгоритма

Основные определения

Объект - поверхность в трехмерном пространстве.

Сцена - четверка {камера, объекты, источники света, видовая плоскость}.

Камера

Камера - "виртуальный глаз", через который мы видим объекты сцены.

Камера задается положением в пространстве и ортонормированным базисом [math]\displaystyle{ (\overline{u}_c, \overline{v}_c, \overline{w}_c) }[/math] (локальная система координат). [math]\displaystyle{ \overline{u}_c }[/math] направлен вправо относительно наблюдателя, [math]\displaystyle{ \overline{v}_c }[/math] - вверх, [math]\displaystyle{ \overline{w}_c }[/math] - на наблюдателя. Таким образом, [math]\displaystyle{ (\overline{u}_c, \overline{v}_c, \overline{w}_c) }[/math] - правая тройка векторов. Вектор направления камеры в своей системе координат: [math]\displaystyle{ (0, 0, -1) }[/math].

Видовая плоскость

Перед камерой на расстоянии [math]\displaystyle{ d }[/math] поставим пиксельную сетку [math]\displaystyle{ w \times h }[/math] с размером пикселя [math]\displaystyle{ s \times s }[/math] так, чтобы камера была направлена в ее центр. Итоговое изображение будет получено из этой сетки покраской пикселей в нужный цвет.

[math]\displaystyle{ p(i, j) }[/math], где [math]\displaystyle{ i=\overline{0,h-1}, \quad j=\overline{0,w-1} }[/math] - пиксел [math]\displaystyle{ i }[/math]-ой строки снизу [math]\displaystyle{ j }[/math]-го столбца слева.

Заметим, что с увеличением [math]\displaystyle{ d }[/math] уменьшается угол обзора; [math]\displaystyle{ s }[/math] влияет на масштаб.

Источники света

Типы источников света:

1) Point light - точка, излучающая свет одиноково во всех направлениях.

2) Directional light - бесконечно удаленный источник света, задается направлением.

3) Area light - объект, излучающий свет одинаково во всех направлениях.

Входные данные

[math]\displaystyle{ scene = \{objects, lights, camera\} }[/math] - сцена, которая будет визуализирована алгоритмом, где

[math]\displaystyle{ objects }[/math] - множество объектов (например сфера, плоскость, треугольник, составной объект и т. п.).

[math]\displaystyle{ lights }[/math] - множество источников света (точечные источники, направленные источники, светящиеся объекты)

[math]\displaystyle{ camera }[/math] - камера наблюдения ("виртуальный глаз").

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

Источники света задаются по-разному, в зависимости от способа распространения света от источника. Например, точечные источники света задаются положением в пространстве и интенсивностью, направленные - интенсивностью и вектором направления.

Камера задается положением в пространстве и ортонормированным базисом [math]\displaystyle{ (\overline{u}_c, \overline{v}_c, \overline{w}_c) }[/math] (локальная система координат). [math]\displaystyle{ \overline{u}_c }[/math] направлен вправо относительно наблюдателя, [math]\displaystyle{ \overline{v}_c }[/math] - вверх, [math]\displaystyle{ \overline{w}_c }[/math] - на наблюдателя. Таким образом, [math]\displaystyle{ (\overline{u}_c, \overline{v}_c, \overline{w}_c) }[/math] - правая тройка векторов. Вектор направления камеры в своей системе координат: [math]\displaystyle{ (0, 0, -1) }[/math].

Генерация луча

Перед камерой на расстоянии [math]\displaystyle{ d }[/math] поставим пиксельную сетку [math]\displaystyle{ w \times h }[/math] с размером пикселя [math]\displaystyle{ s \times s }[/math] так, чтобы камера была направлена в центр сетки. Итоговое изображение будет получено из этой сетки покраской пикселей в нужный цвет.

Пусть [math]\displaystyle{ p(i, j) }[/math], где [math]\displaystyle{ i=\overline{0,h-1}, \quad j=\overline{0,w-1} }[/math] - пиксел [math]\displaystyle{ i }[/math]-ой строки снизу [math]\displaystyle{ j }[/math]-го столбца слева. Проведем луч из камеры к центру пикселя в локальной системе координат камеры. Начало луча в точке [math]\displaystyle{ \overline{pos}=(0,0,0) }[/math]. Найдем вектор направления [math]\displaystyle{ \overline{dir} }[/math]:

[math]\displaystyle{ \overline{dir}.x = s (j -\frac{w}{2} + \frac{1}{2}) }[/math]

[math]\displaystyle{ \overline{dir}.y = s (i -\frac{h}{2} + \frac{1}{2}) }[/math]

[math]\displaystyle{ \overline{dir}.z = d }[/math]

Нормируем вектор [math]\displaystyle{ \overline{dir} }[/math] и переведем луч в глобальную систему координат.

Пересечение луча с объектами сцены

Все точки луча [math]\displaystyle{ ray(\overline{p}, \overline{d}) }[/math] можно представить в виде:

[math]\displaystyle{ h(x,y,z)=\overline{p}+t \cdot \overline{d}, \quad t \in [0;\infty) }[/math].

Луч пересекает объект [math]\displaystyle{ O_i }[/math] тогда и только тогда, когда [math]\displaystyle{ t_i = \inf\{t \gt 0 \, | \quad \overline{p}+t \cdot \overline{d} \in O_i\} \, \lt \infty }[/math]

Если луч пересекает объект [math]\displaystyle{ O_i }[/math], то [math]\displaystyle{ h_i(x,y,z) = \overline{p}+t_i \cdot \overline{d} }[/math] - их первое пересечение, [math]\displaystyle{ t_i }[/math] - расстояние от начала луча до точки пересечения.


Вычисление цвета в точке пересечения

Найдем [math]\displaystyle{ T = \{t_i\} }[/math] - параметры пересечения с объектами (см. предыдущий пункт).

[math]\displaystyle{ t_{min} = \min \{t_i\}, \quad O_{min} = \{O_i | t_i = t_{min}\} }[/math]

Если [math]\displaystyle{ t_{min} = \infty }[/math], то луч не пересекает ни один объект и нужно покрасить соответствующий пиксел в черный цвет (цвет фона).

Если [math]\displaystyle{ t_{min} \lt \infty }[/math], то нужно найти выходящее излучение (цвет) из точки пересечения в камеру. Выходящее излучение зависит от свойств материала объекта, прямого (излучение, исходящее от источников света) и непрямого (отраженный свет) излучений, входящих в точку пересечения; угла между нормалью к поверхности в точке пересечения и направлением луча.

Вычисление прямого излучения

[math]\displaystyle{ h_{min}(x,y,z) = \overline{p}+t_{min} \cdot \overline{d} }[/math].

[math]\displaystyle{ L_{is} = \sum_i L(S_i, h_{min}) }[/math] - прямое входящее излучение.

[math]\displaystyle{ L(S_i, h_{min}) }[/math] - излучение источника света [math]\displaystyle{ S_i }[/math], приходящее в точку [math]\displaystyle{ h_{min} }[/math].

Суммирование ведется по [math]\displaystyle{ i }[/math] таким, что [math]\displaystyle{ S_i }[/math] "виден" из точки [math]\displaystyle{ h_{min} }[/math], в противном случае какой-то объект загораживает источник света, и в точку [math]\displaystyle{ h_{min} }[/math] будет падать тень от этого объекта.

Вычисление непрямого излучения

Рассмотрим упрощенную модель непрямого излучения. Пусть материал объекта [math]\displaystyle{ O_{min} }[/math] обладает способностью отражать свет.

Тогда нужно найти излучение, приходящее со стороны отраженного луча:

[math]\displaystyle{ ray(\overline{h}_{min}, \overline{w}_i) }[/math] - отраженный луч.

[math]\displaystyle{ \overline{w}_i = \overline{d} -2\cdot (\overline{n}, \overline{d})\cdot \overline{n} }[/math].

[math]\displaystyle{ L\{ray(\overline{h}_{min}, \overline{w}_i)\} }[/math] - излучение приходящее со стороны отраженного луча. Оно находится тем же самым алгоритмом, что и цвет пикселя (то есть рекурсивно).

Улучшение визуальных качеств изображения

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

Вычислительное ядро алгоритма

Основные вычисления связаны с:

1) поиском пересечения луча с объектами сцены.

2) расчетом излучения от каждого источника света (требуется определить, "виден" ли источник света из точки пересечения с объектом).

Макроструктура алгоритма

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

Схема реализации последовательного алгоритма

Здесь приведен псевдокод ключевых моментов алгоритма.

//Покраска пикселей изображения

    for (uint r = 0; r < h; r++) //строки
    {
        for (uint c = 0; c < w; c++) //столбцы
        {
            image(r,c) = renderPixel(r, c); //вычисляем цвет в пикселе
        }
    }
    //renderPixel(r, c)

    pixel_color = {0,0,0};

    for (uint j = 0; j < num_rays_per_pixel; j++)
    {
        pp = generatePoint(); //генерируем произвольное смещение внутри пикеля

        //вычисляем координаты точки на полотне
        x = s * (c - 0.5 * w + pp.x);
        y = s * (r - 0.5 * h + pp.y);

        //cоздаем луч
        Ray ray({0,0,0}, normalize({x,y,-d}));
        Ray worldRay = convertToWorldCoords(ray);
        
        pixel_color += traceRay(worldRay, 0) * 1.0 / num_rays_per_pixel; //вычисляем цвет для каждого луча и усредняем
    }
    //traceRay(ray, depth)

    if (depth > max_depth) //превысили максимальную глубину луча, дальше не идем
    {
        return black; //возвращаем черный цвет
    }

    Intersection is = hitObjects(ray); //нашли первое пересечение с объектами сцены и записали информацию о нем в is:
                                       //материал объекта, расстояние, нормаль и т. п.

    if (is) //если есть пересечение
    {
        is.ray = ray; //запомнили луч
        is.depth = depth; // и глубину

        return shade(is); //расчитали цвет в точке пересечения в зависимости
                          //от материала объекта, используя информацию о пересечении
                                        
    }

    return getBackgroundColor(ray); //вернули цвет фона
//shade(is)

//цвета
direct_illumination = {0,0,0};
indirect_illumintaion = {0,0,0};

for (uint i = 0; i < light_count; i++)
{
    if (light[i].isVisible(is)) //если источник света виден из точки пересечения
    {
         direct_illumination = getIllumination(light[i], is); //рассчитываем прямое излучение от i-го источника света
    }
}


if (isReflective(is.object))
{
      indirect_illumination = traceRay(reflecedRay(is.ray), is.depth + 1);
}

//зная входящее излучение, расчитываем выходящее в зависимости от свойств материала объекта
//...

Последовательная сложность алгоритма

[math]\displaystyle{ w }[/math] - ширина полотна

[math]\displaystyle{ h }[/math] - высота полотна

[math]\displaystyle{ depth_{max} }[/math] - максимальная глубина луча в рекурсии (помимо первичного луча)

[math]\displaystyle{ N_{obj} }[/math] - количество объектов сцены

[math]\displaystyle{ N_{lights} }[/math] - количество источников света сцены

[math]\displaystyle{ N_{rays} }[/math] - число лучей на один пиксел

На каждой итерации ищется пересечение луча со всеми объектами: [math]\displaystyle{ O(N_{obj}) }[/math]

После нахождения точки пересечения, выясняется, виден ли каждый источник света из этой точки. Для этого нужно найти первое пересечение теневого луча (начало в точке пересечения, направлен к источнику света) и сравнить расстояние до точки пересечения с расстоянием до источника света. Для всего этого требуется [math]\displaystyle{ O(N_{lights} \cdot N_{obj}) }[/math] операций.

Для каждого луча понадобится [math]\displaystyle{ O(depth_{max}(N_{obj} + N_{obj} \cdot N_{lights})) = O(depth_{max}\cdot N_{obj} \cdot N_{lights}) }[/math] операций.

Всего лучей, выходящих из камеры [math]\displaystyle{ w \cdot h \cdot N_{rays} }[/math].

В итоге получаем [math]\displaystyle{ O(depth_{max}\cdot N_{obj} \cdot N_{lights} \cdot w \cdot h \cdot N_{rays}) }[/math] операций.

Информационный граф

Ресурс параллелизма алгоритма

Входные и выходные данные алгоритма

Свойства алгоритма

Программная реализация алгоритма

Особенности реализации последовательного алгоритма

Локальность данных и вычислений

Возможные способы и особенности параллельной реализации алгоритма

Масштабируемость алгоритма и его реализации

Динамические характеристики и эффективность реализации алгоритма

Выводы для классов архитектур

Существующие реализации алгоритма

RenderMan

V-Ray

NVIDIA IRay

NVIDIA mental ray

Hydra Renderer

Литература

http://www.scratchapixel.com/

http://www.raytracegroundup.com/

http://www.ray-tracing.ru/