Making Of "Адмиралъ". Секреты создания подводных эпизодов

Меня зовут Александр Опланчук, 29 лет. Родился в городе Алма-Ата. Увлекаться 3Д графикой начал в далеком 1994 году, когда у меня появился первый PC 386. Закончив школу поступил в Томский государственный университет на Радио Физический факультет. Проучившись год перевелся в Питер в Политех на ту же специальность. Вспомнил я о 3д графике на 3-м курсе, когда пошел на военную кафедру, нужно было в 3д сделать презентацию работы ЗРК с300. И как то все поехало, закрутилось. Начал усиленно изучать моделлинг в Lightwave3d, устроился в студию в Питере "VideoSfera", делал 3д модели, несложную техническую анимацию для различного рода рекламы.
В 2004 году перешел в студию "Бегемот", которая наверное и дала основной толчок в развитии. Студия занималась в основном постродакшеном различных сериалов и рекламой, бесценный опыт был приобретен в процессе работы над сериалом "Мастер и Маргарита", потребовалось из  3д моделлера\аниматора\rendering стать композером и изучить основы композитинка в Shake и After Effects, узнать о существовании match moving а так же познакомится с Maya, любовь к которой не прекращается и по сей день.
После распада студии "Бегемот" в 2007, образовалась студия "V-jet", в которой я проработал до конца 2008 года. И вот работаю в студии "V-jet" пришло предложение от студии "Dago"(в которую я и перешел в последствии работать после окончания работы над подводными эпизодами) поработать студии над кинофильмом "Адмирал".
В общей сложности необходимо было сделать 6 подводных планов с миной, которые в последствии переросли в 16 =)) Работая  в одиночку от начала и до конца на эти 16 эпизодов было потрачено около 9 месяцев, сюда входит все начиная  текстуринга\освещения\рендеринга\анимации\particle FX dynamics и прочего, заканчивая финальным композитингом.
В процессе работы возникало очень много задач порой с очень нетривиальным решением, и об одной такой задачи я хотел бы вам рассказать ниже.

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

Итак приступим.

1. У нас есть базовая сцена в которой есть мина качающаяся на волнах. Отображение oceanShader реализуется посредством Preview Plane который расположен Dynamics-> Fluide Effects -> Ocean -> Add Preview Plane   Создайте Preview Plane   и средней кнопки мышки перетащите в область displacement ваш oceanShader.

2.Создаем nurbs plane с достаточной детализацией. Чем плотнее сетка тем точнее будет выглядеть кривая соприкосновения мины и поверхности океана. После создания и перемещения nurbsplane в то место где у нас мина идем в Modify->Freeze transform и замораживаем трансформации по всем параметрам.

3. Выделяем нурбс плеин и идем закладки dynamics->Soft/Rigid Bodies->Create Soft body->Make soft. В результате появится particle shape на нашем Nurbs Plane. Выбираем его. Идем в свойства Particle Shape в раздел Per Particle (Array ) Attributes. ПКМ на сторчке Position-> Runtime Expression After dynamics и добавляем туда следующие строчки:

vector $ppos =nurbsPlane2ParticleShape.position;
float $pu = $ppos.x;
float $pv = $ppos.z;
float $cpoint[] =`colorAtPoint -u $pu -v $pv WhiteCaps_oceanShader1`;
float $py = $cpoint[0];
nurbsPlane2ParticleShape.position = <<$pu, $py, $pv>>;

где nurbsPlane2ParticleShape - название вашего Particle shape

WhiteCaps_oceanShader1 - название вашего ocean Shader.

4. Так как операции по вычислению положения каждой вершины нурбса занимает достаточно много времени то на данном этапе нужно сделать Bake simulation. Для этого в режиме F8 выбираем все контрольные точки nurbs plane и идем в звкладки edit->keys->Bake simulation Hierarchy - Selected Channels - All keyable Contol points Shapes и нажимаем Bake. Теперь можно пойти выпить чаю, так как процесс не быстрый и зависит от того насколько плотную сетку вы в начале сделали на nurbs plane. После процесса bake Particle Shape нужно удалить. То есть теперь мы имеем nurbs plain который деформится так как мы настроили в ocean Shader.

5. Дальше нужно создать nurbs поверхность вашего объекта на волнах. В моем случае это обычный nurbs Sphere, который посредством констрейна прилинкован к мине. После того как вы создали его необходимо получить кривую пересечения поверности океана и только что созданного мной nubs Sphere. Для этого выбираем их обоих и идем в Surfaces-> Edit Nurbs-> Intersect Surfaces настройки показаны ниже на картинке.

После создания кривой выбираем ее и идем в Edit Curve->Rebuild Curve. Настройки показаны на картинке ниже.

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

6. Далее переходим к созданию эмиттера частиц. Выбираем нашу кривую и идем Dynamics->Particles->Emit from objects настройки как на картинке ниже.

Выделяем созданный эмиттер  Particles->Per-Point EmissionRates. Создаем вспомогательный локатор на который мы выведем некоторые параметры для более удобного контроля.В моем случае это locator2. Добавим на него несколько экстра атрибутов. Treshhold и max_rate. Их значения подбираются имперически исходя из потребностей, особенно это касается параметра Treshhold.

7.Для управления PP Emission rate создаем еще 1 партикал шейп для этого идем Particles-> Particle Tool   Particle Name - emission_control Number of particles = количество CV точек в вашей кривой. Идем в атрибуты созданного particle shape в раздел Per Particle (Array ) Attributes. ПКМ на сторчке Position-> Runtime Expression After dynamics и добавляем туда следующие строчки:

int $i = emission_controlShape.particleId;
float $rt = `getAttr "locator2.Max_rate"`;//Берем атрибуты с ранее созданного локатора
float $rndrate= rand(-$rt,$rt);
$Porog = `getAttr "locator2.treshhold"`;"`;//Берем атрибуты с ранее созданного локатора
float $V = `getAttr  -time (frame+0.5) intersectionCurve1_1rebuiltCurveShape1.cv[$i].yValue`-`getAttr  -time (frame) intersectionCurve1_1rebuiltCurveShape1.cv[$i].yValue`;
if (abs($V) > $Porog )
setAttr intersectionCurve1_1rebuiltCurveShape1.emitter1RatePP[$i] $rndrate;
else
setAttr intersectionCurve1_1rebuiltCurveShape1.emitter1RatePP[$i] 0;

Теперь у нас эмиссия на кривой зависит от значений treshhold и max_rate которые у нас выведены на locator2.

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

createNode closestPointOnSurface;
createNode pointOnSurfaceInfo;

Теперь нужно соеденить shape нашего плавающего nurbs объекта (nurbsSphereShape1) с этими 2-мя нодами. Для этого выбираем closestPointOnSurface1 и nurbsSphereShape1(наш нурбс объект) идем Window->Hypergraph: Connections и соединяем nurbsSphereShape1.worldSpace to closestPointOnSurface1.inputSurface  

Либо в script editor вводим эту команду заменя имя на ваш Nurbs object.

connectAttr -f nurbsSphereShape1.worldSpace[0] closestPointOnSurface1.inputSurface;
connectAttr -f nurbsSphereShape1.worldSpace[0] pointOnSurfaceInfo1.inputSurface;

9. На particle1 (наши пенные частицы) добавляем creation expression:

particleShape1.mass= rand (0.8,1);//Создание рандомной массы
vector $pos = particleShape1.position;
float $pozx = $pos.x;
float $pozy = $pos.y;
float $pozz = $pos.z;
//определение нормали и скоростей
setAttr "closestPointOnSurface1.inPositionX" $pozx;
setAttr "closestPointOnSurface1.inPositionY" $pozy;
setAttr "closestPointOnSurface1.inPositionZ" $pozz;
float $u = `getAttr "closestPointOnSurface1.u"`;
float $v = `getAttr "closestPointOnSurface1.v"`;
setAttr "pointOnSurfaceInfo1.parameterU" $u;
setAttr "pointOnSurfaceInfo1.parameterV" $v;
float $velosx = `getAttr "pointOnSurfaceInfo1.normalX "`;
float $velosy = `getAttr "pointOnSurfaceInfo1.normalY "`;
float $velosz = `getAttr "pointOnSurfaceInfo1.normalZ "`;
float $rnd = rand(10,40);
particleShape1.velocity = <<$velosx*$rnd,$rnd,$velosz*$rnd>>;
так же добавляем runtime after dynamics expression:
vector $pos = particleShape1.position;
float $px = $pos.x;
float $pz = $pos.z;
float $disp[] = `colorAtPoint -u $px -v $pz WhiteCaps_oceanShader1`;
float $py = $disp[0];
float $yDiff = $py - $pos.y;
float $suckForce = 0.12;
float $minElevation = 0.05;// min height of foam above water.
$py = $pos.y + $yDiff * $suckForce;
if( $py < $disp[0] + $minElevation){
$py = $disp[0] + $minElevation;
}
particleShape1.position=<<$pos.x, $py, $pos.z>>;
particleShape1.velocity= particleShape1.velocity;

10. Выставляем Lifespan Mode - Random range, Lifespan - 2 , Life span Random 0,7 (это будит время жизни пены в секундах)

В звкладке Render Attributes выставляем Particle render type - MultiPoint multi radius - 1.4 Point size -1.

Не забываем выставить Conserve <1 для того чтобы частицы теряли энергию в моем случае 0.94.

Добавляем PP opacity и color. На колор вешаем градиент белого цвета. На opacity градиент от белого до черного, в завистимости от времени жизни частиц.

Добавляем поле гравитация и турбуленция. Параметры подбираем исходя из масштаба сцены.

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

Ну а мой результат пока я писал этот урок выглядит вот так:

Финальный композитинг, именно эта мина фигурировала в уроке:

Сайт автора: www.trisen.info

700 0 850 8
9
2010-09-28
интересно. правда realflow был бы здесь больше к месту.
2010-09-28
рф стоит 4000 баксов, а я думаю у них стоит лицензионный софт
2010-09-28
[quote=[hidden]] [/quote] Реалфлоу тут был просто не в тему, задача не требует гидродинамики. Довольно просто решается в мае.
2010-09-28
dreamscape toje spravilsa bi i particleflow... i bez etih ujastnih skriptov, boyus ya ih :)))))))))
2010-09-28
oi, ya prognal :)))))))) ya dumal re4 o 3ds max!!!! moi izvenenia :)))))))))))
2010-09-29
Молодчина! Только зарегистрировался, и уже с уроком
2011-06-10
Классный урок, а может кто-нибудь помочь мне расшифровать вот эту строчку float $V = `getAttr -time (frame+0.5) intersectionCurve1_1rebuiltCurveShape1.cv[$i].yValue`-`getAttr -time (frame) intersectionCurve1_1rebuiltCurveShape1.cv[$i].yValue`; Что означает на кривой - ".yValue", немогу понять Это пункт 7, спасибо!
2011-06-13
[quote=Тридэшник] Классный урок, а может кто-нибудь помочь мне расшифровать вот эту строчку float $V = `getAttr -time (frame+0.5) intersectionCurve1_1rebuiltCurveShape1.cv.yValue`-`getAttr -time (frame) intersectionCurve1_1rebuiltCurveShape1.cv.yValue`; Что означает на кривой - ".yValue", немогу понять Это пункт 7, спасибо! [/quote] ".yValue" - означает координату CV точки кривой по вертикальной оси Y. А в целом выражение выше высчитывает скорость каждой CV точки относительно текущего кадра и кадра + 0.5, впоследсвии это используется как некий threshhold для эмиссии.
2011-06-14
[b]Trisen[/b] Большое спасибо! Разбираюсь дальше!
RENDER.RU