- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF132:pov-ray
Материал из Linuxformat.
- POV-Ray Создавайте новые миры всего несколькими строчками кода
Содержание |
POV-Ray: Строим новый мир
- Трехмерное моделирование – это не только Blender. Вячеслав Ястребцев представляет другие рендереры, дающие новые возможности.
POV-Ray |
---|
|
Принято считать, что Linux обделён мультимедийными приложениями. До последнего времени это было обоснованным мнением, однако сегодня сообщество активно разрабатывает многочисленные программы для создания и просмотра мультимедиа. Трехмерные приложения – бесспорный лидер этого процесса. Стремительно развивающийся редактор трёхмерных сцен Blender (http://www.blender3d.org), используемый для создания трёхмерной графики и анимации профессионального качества; продвинутый рендерер YafAray (http://www.yafaray.org); наконец, вершина современных технологий визуализации – LuxRender (http://www.luxrender.net), обеспечивающий отрисовку изображений, практически неотличимых от фотографий – все они обещают скорый бум свободного ПО в медиа-индустрии. Но в тени стремительно растущих современных проектов тихо, без громких обещаний, развивается, пожалуй, самая почтенная система 3D-моделирования и визуализации, уходящая своими корнями в далёкие 80‑е, но достойная пристального внимания даже сегодня. Имя этому аксакалу – POV-Ray.
Приготовимся к старту
Как и всякий старец, POV-Ray отличается консерватизмом и непростым характером. Двоичная версия релиза 3.6, доступная на http://www.povray.org, вышла аж в 2004 году, и её нормальная работа в современных дистрибутивах не гарантируется. Лучше сразу скачать исходный код POV-Ray 3.7 Beta и собрать его самостоятельно. Тут нас поджидают дополнительные хлопоты: при запуске конфигурационного скрипта требуется указать своё имя с помощью опции COMPILED_BY=«имя» (подойдёт любая комбинация букв и цифр). Кроме того, бета-версия требует постоянного ввода некого кода. Для его получения необходимо ввести в консоли команду povray --betacode; программа напечатает набор символов, который необходимо присвоить переменной POVRAY_BETA и далее экспортировать, выполнив export POVRAY_BETA=код.
Установив программу, настройте параметры рендеринга. Откройте файл ~/.povray/3.7/povray.ini и добавьте в его конец две строки: Pause_When_Done=On, чтобы окно с готовым изображением не пропадало по завершении отрисовки, и Output_File_Name=«pov_render.png» – она указывает, в какой файл записывать созданное изображение.
Лицензии, лицензии
Неприятный сюрприз: POV-Ray не является свободным ПО. Да, исходные тексты доступны, и вы можете найти программу в репозиториях своего дистрибутива; но в 1986 году, когда была начата работа над проектом, GPL еще не была столь популярна. За прошедшие 24 года над POV-Ray успело потрудиться множество людей, поэтому изменить лицензию на более приемлемую в современном Linux-мире, увы, не представляется возможным.
Азбука SDL
Итак, всё готово для знакомства. POV-Ray не имеет встроенных средств интерактивного моделирования: есть множество программ, экспортирующих в его формат (в том числе альфа-версия Blender 2.5). Однако всю мощь POV-Ray можно раскрыть, только описывая сцену на SDL (Scene Description Language) – интерпретируемом языке программирования с С-подобным синтаксисом.
Сцены POV-Ray состоят из объектов, описываемых единым образом. Сам объект задаётся конструкцией вида:
тип_объекта {параметры}
Параметры бывают двух видов: обязательные и дополнительные. Обязательные необходимо указывать при создании объекта, сразу после открывающей фигурной скобки. Если забыть это сделать, POV-Ray сообщит об ошибке и аварийно завершит работу. Обязательные параметры могут быть числами или векторами, имеют фиксированный порядок и разделяются запятыми. Дополнительные параметры, напротив, требуют указания имени параметра, за которым следует его значение (число, вектор или объект). Разделять дополнительные параметры запятой не нужно.
Вектора в нотации POV-Ray записываются так:
<несколько чисел через запятую>
Примером вектора может служить <1, 3, -2> или <0.2, 0.4, 0.9, 0.1>. Вектора используются для обозначения координат и цветов.
Для ускорения работы POV-Ray позволяет использовать ряд сокращений: x, y, z – единичные вектора, совпадающие с соответствующими осями координат (x заменяет вектор <1, 0, 0>, y – <0, 1, 0>, z – <0, 0, 1>); при вводе дробей с нулевой целой частью можно не писать ноль перед точкой (.1 вместо 0.1).
Следует отметить различия координатной системы POV-Ray и Blender: в последнем ось z направлена в зенит, а вращение объектов происходит по часовой стрелке (если смотреть по направлению оси вращения). В POV-Ray ось z направлена к горизонту виртуального мира, а вращение происходит против часовой.
Вооружившись этими простыми правилами, приступим к написанию нашей первой сцены. Создайте текстовый файл с именем sphere.pov, откройте его в любом текстовом редакторе (Vi, Emacs, Kate «понимают» синтаксис SDL) и введите следующий текст:
camera { location <0, 0, -4> look_at <0,0,0> angle 50 } light_source { <4,4,-3>, rgb 1 } sphere { <0, 0, 0>, 1 pigment { color rgb x } }
В первой строке создаётся камера и определяются её основные характеристики: положение, отслеживаемая точка и угол поля зрения, соответственно. Положение камеры и точки обзора задаётся векторами. Как видите, ничего сверхъестественного нет – точно те же параметры (если не больше) мы бы указали, размещая камеру в Blender. В шестой строке создаётся источник света, которому требуются два обязательных параметра: вектор, задающий положение лампы, и цветовой вектор, задающий окраску и интенсивность света. На последнем стоит остановиться подробнее. Строго говоря, цвет в POV-Ray описывается пятью числами: привычной RGB-триадой основных цветов, пропусканием [transmit] и фильтрацией [filter]. Ключевое слово rgb подсказывает POV-Ray, что мы определяем только RGB-компоненты цвета, а пропускание и фильтрация будут нулевыми. Внимательный читатель может воскликнуть: «Цвет задаётся вектором, а в коде после rgb стоит число!» Ничего страшного в этом нет: POV-Ray догадается, что вы имели в виду, и заменит его на вектор требуемой размерности, все компоненты которого будут равны указанному числу. В нашем случае, получится вектор <1, 1, 1> обозначающий чистый белый свет.
Наконец, в девятой строке создаётся сфера. Указываются два обязательных параметра: положение (вектор) и радиус (число), а далее идёт объявление нового объекта «пигмент» (pigment), вложенного в сферу. Пигменты задают цвет поверхности объекта-родителя: без них POV-Ray успешно отрисует сцену, но сфера будет выглядеть чёрным кругом. Внутри пигмента содержится параметр color. Здесь есть ещё одна причина для удивления: нам нужно задать цвет, а переменная x вроде бы описывает координаты? Все в порядке: x – это просто псевдоним для вектора <1, 0, 0>, что бы ни значили его компоненты; в данном случае они определяют красный цвет.
Созданную сцену необходимо скомпилировать (отрисовать). Откройте консоль, перейдите в каталог, содержащий файл sphere.pov, введите команду povray sphere.pov, и на экране появится изображение сферы. Вдоволь налюбовавшись на свое первое творение, щелкните по окну, чтобы закрыть его.
Мир без полигонов
Многие читатели, уже хорошо знакомые с трёхмерной графикой, могут заинтересоваться количеством полигонов в отрисованной сфере: уж больно гладкая у неё поверхность. Сообщаем: ни одного! Для описания сцены POV-Ray применяет математические функции, на ходу рассчитывая точки пересечения лучей света с идеально гладкими поверхностями их графиков, и хотя полигональные объекты можно использовать в сценах, они служат только для импорта моделей из сторонних приложений. С одной стороны, такой подход к моделированию непривычен, с другой – мы избавлены от многих «узких мест»: выбора между высоким качеством и объёмом занимаемой памяти, видимых изломов на поверхностях при достаточно малом расстоянии от камеры до объекта; наконец, сложные поверхности (например, горные хребты), требуют для корректного отображения просто чудовищного числа полигонов.
В POV-Ray существует два основных способа моделирования: с помощью объединения базовых форм (сфер, цилиндров, кубов и т. д.) в более сложные объекты, либо путем создания собственных функций, описывающих сложную поверхность. Первый подход удобно применять для разнообразных технических изделий – деталей механизмов, зданий; второй же идеален для создания ландшафтов. Сегодня мы подробнее остановимся на моделировании с помощью функций, а механикой позанимаемся в следующей части.
Небо и земля
Поставим себе задачу изобразить планету радиусом около 6 000 условных километров, поднять на ней горы повыше Джомолунгмы, налить океан, прикрыть её атмосферой толщиной в десяток километров, а в довершение – вывести виртуальную камеру на орбиту и сфотографировать пейзаж. Не нужно бежать искать терабайтный винчестер – всё уместится в несколько килобайт!
Для лучшего контроля за визуализацией нашей сцены, создадим новый файл с настройками рендерера. Просто скопируйте planet.ini с LXFDVD в директорию с вашим проектом – основные настройки POV-Ray в нем снабжены подробными комментариями; советую прочитать и их.
Некоторые величины, описывающие геологию планеты (радиус, высота гор и т. д.), нам понадобятся не раз, и лучше сразу дать им понятные имена, чтобы не запутаться. Для определения имён разнообразных объектов в POV-Ray используется оператор #declare. Допишите в файл planet.pov следующие строчки:
#declare atmoradius = 6020; #declare planetradius = 6000; #declare reliefheigth = 30; #declare oceandeep = 11;
Как всегда, хорошим тоном будет сразу разнести основные компоненты нашей сцены (материалы, функции и сами объекты) по отдельным файлам. Скажем, создайте файлы planet_texture.inc и planet_functions.inc, а в planet.pov добавьте строки
#include «planet_functions.inc» #include «planet_texture.inc»
Первую из них придется продублировать и в начале файла planet_texture.inc. Ключевое слово #include велит POV-Ray перед дальнейшей обработкой сцены открыть и прочитать указанный файл.
Приступим к созданию объектов. Добавьте в planet.pov следующие строчки, создающие камеру и источник света:
camera { location z*-12000 look_at 0 angle 90 } light_source { x*15000, rgb 1 rotate y*40 }
Выражение z*-12000 обозначает умножение вектора z на число -12 000, т.е. перемещение камеры на 12 000 единиц назад от центра мира (z – это вектор <0, 0, 1>, значит, z*-12000 – <0, 0, -12000>).
По умолчанию, в сцене POV-Ray присутствует рассеянное освещение для смягчения тени, но в космосе рассеянного света нет, поэтому нам следует отключить его:
global_settings { ambient_light 0 }
Теперь перейдите в файл planet_functions.inc – мы приступаем к созданию поверхности планеты. Сама она будет иметь форму шара, на котором располагаются микроскопические (относительно размеров планеты) неровности – горы. Для описания планеты необходимы минимум две функции: первая задает сферическую поверхность, вторая – создает рельеф. Нам же потребуется ещё одна: прибрежные области, как правило, имеют ровный, плоский рельеф, а в горах много провалов, трещин и изломов – третья функция будет отвечать за пересечённость местности.
Время кодировать
Начнём с создания сферы. Добавьте в planet_functions.inc строку:
#declare planetoid = function { sqrt( pow(x,2) + pow(y,2) + pow(z,2) ) }
Она начинается с уже знакомого нам ключевого слова #declare, после которого идёт имя определяемого объекта. Слово function за знаком равенства создаёт новый объект-функцию, математическое выражение для которой приведено в фигурных скобках. Если у вас есть вопросы – обратитесь к полному листингу на LXFDVD; он снабжен подробными комментариями. Необходимо отметить, что переменные x, y и z внутри функций меняют своё поведение: теперь это не вектора единичной длины, а координаты точки в пространстве, для которой вычисляется значение функции. В нашем случае, значением функции будет расстояние от точки в пространстве до центра сцены, а множество равноудалённых от центра точек образуют сферу.
Чтобы сделать сферу видимой, нужно превратить абстрактную формулу в поверхность. Для этого применяется специальный объект – изоповерхность (isosurface), делающий видимыми области, в которых функция принимает определённое значение. Вернитесь в файл planet.pov и припишите к нему:
isosurface { function { planetoid(x,y,z) } threshold planetradius accuracy .000000001 max_gradient 1.6 contained_by { sphere { 0, planetradius+150 } } pigment { biosphere } }
При создании объекта isosurface необходимо указать функцию, на основе которой будет строиться поверхность. У нас это planetoid(), определённая ранее в файле planet_functions.inc. Следующий параметр – threshold (порог), сообщающий, через область с каким значением функции пройдёт будущая поверхность. Мы используем здесь ранее заданную переменную planetradius, значение которой равно 6000 – это радиус сферы. Далее идут два важнейших для создания нормального изображения параметра: accuracy (точность) и max_gradient (максимальный градиент). Если их значения будут не оптимальны, мы получим чрезмерно долгую отрисовку с артефактами (тёмные полосы, дыры в поверхностях и т. д.). Легче всего настроить максимальный градиент: если он слишком мал или велик, POV-Ray напечатает в консоли предупреждение, предложив оптимальное значение. С accuracy сложнее: точность не должна быть ни слишком высокой, ни слишком низкой. Обычно требуются небольшие значения (порядка нескольких тысячных), а если очень малая accuracy не помогает избавится от артефактов, а, наоборот, усиливает их – это повод начать её увеличение.
Параметр contained_by задаёт объект, ограничивающий пространство, в котором может располагаться изоповерхность. В нашей сцене ограничителем служит сфера с радиусом на 150 единиц большим, чем у планеты. Последний параметр назначает для нашей изоповерхности текстуру – это пигмент biosphere, который пока что определен в planet_texture.inc следующим образом:
#declare biosphere = pigment { color rgb .6 }
Посмотрите, что получилось: запустите в консоли povray planet.ini (или взгляните на рисунок). Да, это простой серый шар, но теперь мы можем деформировать поверхность, чтобы создать рельеф.
Поднимем горы
Вернитесь в файл planet_functions.inc и определите функцию, описывающую горы:
#declare highland = function { pattern { crackle warp { turbulence .4 octaves 4 lambda 2 omega .7 } scale planetradius*.025 } }
Здесь используется метод определения функции через встроенные в POV-Ray генераторы-паттерны [pattern], другими словами – процедурные текстуры. Функции, задаваемые с помощью паттернов, возвращают значения от 0 (чёрный цвет на текстуре) до 1 (белый цвет). В нашем случае используется паттерн crackle (аналог текстуры voronoi в Blender); слово warp определяет искажение базовой формы, turbulence – тип и силу искажения; octaves, lambda и omega — дополнительные параметры; scale – модификатор, изменяющий размер рисунка (паттерны, их модификаторы и процедурные текстуры будут детально рассмотрены в следующей статье, а самые нетерпеливые могут обратиться к комментариям в файле на диске).
Создадим еще одну функцию, описывающую контуры континентов и характер рельефа (bozo – близкий родственник Blender-текстуры по имени clouds):
#declare lowlands = function { pattern { bozo warp { turbulence 1 octaves 4 lambda 2 omega .4 } scale planetradius*.3 } }
Наконец, объединим две предыдущие функции в одну, описывающую весь рельеф планеты:
#declare landscape = function {(lowlands(x,y,z)+highland(x,y,z)*pow(lowlands(x,y,z),4))*.7}
Осталось лишь применить созданную функцию к изоповерхности. Вернитесь в файл planet.pov и замените соответствующую строку на
function { planetoid(x,y,z) - landscape(x,y,z)*reliefheigth }
Снова запустите отрисовку командой povray planet.ini. Теперь она будет идти довольно долго, но вместо голого шара появится нечто, испещрённое горами и ущельями. Рельеф планеты готов!
Иногда POV-Ray 3.7 Beta выдает сообщение об ошибке: «Parse Error: Redeclaring functions is not allowed - #undef the function first!». Создаваемая функция считается уже объявленной, что вызывает аварийное завершение работы. Проблема решается путём добавления команды #undef <имя функции> перед объявлением последней.
Вдохнем жизнь
Как мёртвый каменный мир, наша планета неплохо смотрится уже сейчас, но куда интересней она будет выглядеть с атмосферой, водоёмами и лесами. Давайте нальем океаны – для этого нужно просто создать в planet.pov сферу со следующими настройками:
sphere {
0, planetradius+oceandeep
pigment { color rgb <0,.05,.5> }
}
Теперь перейдём в файл planet_texture.inc и как следует разукрасим нашу планету. На Земле вершины гор скрыты льдом, сами скалы лишены растительности и окрашены в красно-коричневые оттенки, равнины покрыты густой зеленью, а прибрежные зоны – песком или галькой; нам нужно воспроизвести это чередование цветов. Вначале зададим основные цвета, вписав перед определением пигмента biosphere строки:
#declare silt = color rgb .7; #declare beach = color rgb <.5, .45, .05>; #declare forests = color rgb <0, .2, .02>; #declare rock = color rgb <.2, .08, .02>; #declare ice = color rgb .8;
Далее надо изменить сам пигмент biosphere следующим образом:
#declare biosphere = pigment { function { landscape(x,y,z) } color_map { [0 color silt] [.36 color silt] [.365 color beach] [.37 color beach] [.38 color forests] [.48 color forests] [.54 color rock] [.55 color rock] [.56 color ice] [1 color ice] } }
Здесь мы воспользовались объектом color_map чтобы изменять цвет поверхности в зависимости от значения функции landscape().
Наконец, добавим атмосферу, определив в файле planet.pov объект sphere с параметрами:
sphere { 0, atmoradius hollow material {atmosphere} }
Атмосфера рассеивает и поглощает солнечный свет. В объявлении сферы использовано ключевое слово hollow, которое подготавливает объект к имитации объёмной среды, рассеивающей свет. Вместо пигмента задан материал (material) – сложнейший объект, объединяющий все оптические свойства предмета: от цвета поверхности до подповерхностного рассеивания света.
Перейдём в файл planet_texture.inc и добавим в нём еще один интересный материал, следующего вида:
#declare atmosphere = material { texture { pigment { color rgbt 1 } } interior { media { scattering { 3, rgb <.1,.8,1>*.003 } absorption rgb <.1,.8,1>*.003 } } }
Запустив отрисовку, мы увидим голубоватую дымку, привычно окутывающую планету.
Запустим спутник
То, что у нас получилось – это не просто шар с текстурой! Замените старое описание камеры на следующее:
camera {
#local camLoc = -6200 * z;
location camLoc
sky -x
look_at camLoc - x + .3*z
angle 90
rotate <-26, -.8, 0>
}
Камера подойдёт ближе к поверхности планеты (по земным меркам, высота над поверхностью составит около 150 километров) и повернётся к одному из хребтов. Выполните отрисовку еще раз – и вы увидите горную цепь с ущельями, напоминающими русла рек. Подобные пейзажи можно найти по всей виртуальной планете: просто изменяйте вектор после слова rotate, чтобы переезжать с места на место.