EN
Andrey Sokolov
Andrey Sokolov
730 subscribers
goals
21 of 500 paid subscribers
Когда я наберу 500 платных подписчиков, смогу больше времени уделять записи видео и разработке аддонов для Blender.
359.48 of $ 1 131 money raised
Донаты || Donates
37.45 of $ 566 money raised
Cделать аддон True Time Remapping бесплатным для всех желающих навсегда. Make True Time Remapping add-on free for everyone forever.
113.08 of $ 114 money raised
На дисковый накопитель 4Tb для хранения бэкапов курса «Blender Избушка».

Волосы на геонодах ► 03. Наклон волос

Следующая задача - регулировка угла наклона волос.
Буду очень благодарен всем, кто решит поддержать проект материально. В данный момент это можно сделать только здесь, на Boosty, сделав разовый платёж или оформив спонсорскую подписку приемлемого для вас уровня (от 200 р. в месяц).   
ОБЩАЯ ИДЕЯ
В первой части, во время добавления волос, мы причесали их таким образом, чтобы все волосы были наклонены по отношению к поверхности объекта-эмиттера. Для контроля над системой волос может быть удобным процедурно регулировать, этот угол наклона. Высчитывать мы будем его с помощью нормали полигона. Таким образом вращение, фактически заключающееся в передвижении конца волоса, будет осуществляться в плоскости, образованной наклонённым волосом и нормалью поверхности эмиттера ↓
Оговорюсь сразу, что у нас не стоит задачи свободно вращать волос на 360 градусов в этой плоскости, иначе решение бы отличалось. Задача, чтобы можно было наклонять волос или поднимать его от поверхности, и для этой цели 90 градусов в обе стороны будет более чем достаточно. В варианте, который я предлагаю ниже, на границах этого диапазона эффект будет постепенно замедляться, что на практике для управления может быть удобнее, чем линейное вращение.
Для реализации нам сначала нужно высчитать вектор, перпендикулярный векторам волоса и нормали полигона. Это можно будет сделать с помощью перекрёстного умножения (Cross Product) векторов ↓ 
Затем высчитать с помощью перекрёстного произведения двух векторов - полученного на прошлом шаге вектора и вектора нормали полигона - вектор, перпендикулярный им обоим. Он окажется в той же плоскости, которую образуют вектор нормали полигона и вектор волоса. И если вычесть его из вектора волоса, мы получим третий вектор, который сможем использовать для сдвига конца волоса в нужной плоскости ↓
Плоскость останется той же, но при сдвиге конца волоса с помощью полученного вектора длина нового волоса может сильно меняться. Зелёным диагональным пунктиром обозначена линия, по которой будет передвигаться конец волоса ↓
Однако, зная новое направление вектора волоса, мы легко сможем компенсировать его длину, умножив новый вектор волоса на отношение между длиной вектора изначального волоса и длиной нового вектора волоса. То есть мы просто высчитаем длину волоса до и после наклона, поделим первый результат на последний и умножим на полученное число вектор нового волоса ↓
Там будут и другие тонкости, но с ними мы будем разбираться уже по ходу действия.
ПРАКТИЧЕСКАЯ РЕАЛИЗАЦИЯ
Итак, для начала нам понадобятся:
• вектор между началом и концом волоса, для краткости будем называть его вектором волоса
• вектор нормали полигона
Вектор волоса мы уже высчитывали в прошлом уроке, и есть предположение, что он нам может понадобиться ещё и для других задач. Как и в программировании, где повторяющийся код объединяют в функции, которые можно использовать повторно, в нодовых редакторах повторяющиеся связки нодов объединяют в группы. Зайдём с помощью Tab в группу GN Hair Length и выделим все ветки нодов, которые приходят в Vector Math в режиме Subtract, включая его самого - это и есть поле (список значений) векторов волос, разностей между координатами их концов и начал ↓
Кстати, в комментариях к прошлому уроку подсказали ещё один способ получения координат начал волос. У меня все скриншоты до конца серии уже подготовлены, поэтому в этой серии я уже переделывать эту схему не буду, но вы можете использовать любую из этих сборок, какая больше понравится.
Блендер позволяет делать вложенные группы - группы внутри групп, найти их после создания можно, как и любые нод-группы, в Shift+A > Group. Выделенные ноды объединяем в группу с помощью Ctrl+G. Входы в этой группе не нужны, она всегда будет возвращать векторы волос той геометрии, к которой ветка, включающая эту группу, будет подключена ↓
С помощью Tab выходим на уровень вверх, ставим щиток, чтобы защитить группу от случайного удаления и называем её Hair Vector или GN Hair Vector
Реаранжируем ноды для больше читаемости ↓
🛈 В нод-группах с несколькими уровнями вложенности клавиша Tab перемещает нас по уровням вложенности, в зависимости от того, какие ноды в данный момент выделены. Если выделена группа внутри группы, то по нажатию Tab мы переместимся в неё. Если выделен любой другой нод, то мы выйдем из текущей группы на один уровень вложенности вверх.
Выйдем из нод-групп и добавим созданную группу в общее дерево с помощью Shift+A > Group > Hair Vector (или то название группы, которое вы назначили ей выше) ↓
Следующий шаг - получение поля с векторами нормалей объекта-эмиттера. Чтобы получить доступ к его геометрии, включающей, как и в случае с волосами, все её параметры - координаты точек, нормали и т.д. - нам нужен нод Object Info (находим по названию через Shift+A > Search или напрямую в категории Input) ↓
С помощью этого нода можно получить доступ к геометрии других объектов. Для этого нужно указать нужный объект в соответствующем поле. Сейчас мы работаем с объектом Curves, а нам нужен объект Cube (если вы его не переназывали) - это тот объект, к геометрии которого привязаны волосы ↓
Переключим Original на Relative, что позволит использовать данные геометрии, с учётом его родительских взаимосвязей. Например, если объект привязан в качестве чайлда к другому объекту-пэренту, чтобы мы использовали его геометрию в том виде, в котором видим её во вьюпорте, а не в том виде, в котором она существовала бы, если бы не была привязанной к другому объекту. Не знаю, почему Relative не сделали выбором по умолчанию.
В Геометрических Нодах любые данные любых элементов называются атрибутами (англ. Attribute). У точек есть позиции в пространстве - это их атрибуты. У объектов есть имена - это их атрибуты. У сплайнов в объекте-кривых есть индексы - это их атрибуты. Атрибуты элементов можно получать с помощью других нодов, атрибуты для элементов можно создавать и существующие атрибуты элементов можно переносить с геометрии на геометрию.
Нормали - это атрибут объекта Cube, объекта-эмиттера. Для того, чтобы переносить на нужную нам геометрию атрибуты геометрии другого объекта, используется нод Transfer Attribute. Поскольку нам нужно будет в качестве атрибута переносить нормали, которые являются векторами, типа данных нужно переключить с Float (дробные значения с плавающей запятой) на Vector (вектор) ↓
Ко входу Attribute подключаем нод Normal из категории Input
Эта связка и будет определять поле с нормалями поверхности объекта-эмиттера. Можно с помощью Ctrl+J объединить ноды в рамочку и в N-панели, во вкладке Node задать ей цвет (я обычно выставляю значение Value в настройке цвета на 0.3, потому что рамочка без цвета сливается с фоном, а цвет по умолчанию сливается с текстом) и название, например Normal Vector ↓
На визуальную организацию рабочего пространства всегда имеет смысл потратить пару лишних минут. Чтобы когда проект откроет кто-то другой, кто будет продолжать с ним работать, или даже вы сами, спустя всего пару дней или неделю или месяц или год - словом, чтобы любой мог быстро прочитать, что где и для чего происходит. Поэтому предлагаю назвать вектор волоса Hair (Vector a), а вектор нормалей Normal (Vector b). И теперь, согласно плану, нам нужно рассчитать вектор, перпендикулярный им обоим (обозначен жёлтым). Вернее, нужно сказать Блендеру, чтобы он его рассчитал, потому что для этого есть Vector Math в режиме Cross Product (перекрёстное произведение векторов) ↓
Следующим шагом нам нужно найти вектор, который будет находиться в одной плоскости с векторами волоса и нормали, но будет составлять с вектором нормали угол 90 градусов (обозначен зелёным). Для этого нам нужно найти вектор, перпендикулярный одновременно перекрестному произведению векторов нормали и волоса и самому вектору нормали. Иными словами, нам нужен второй Vector Math в режиме Cross Product (перекрёстное произведение векторов) ↓
Теперь нам нужно найти вектор между концом волоса и концом получившегося на прошлом шаге вектора. Его мы будем использовать для сдвига конца волоса. Чтобы его найти, нужно из вектора волоса вычесть полученный на прошлом шаге вектор. Это делается при помощи Vector Math в режиме Subtract (вычитание) ↓
Управлять добавляемым наклоном мы будем, регулируя размер вектора с помощью Vector Math > Scale: на нуле наклон будет оставаться прежним, при положительных значениях будет усиливаться, при отрицательных - уменьшаться. В принципе, можно использовать этот вектор для сдвига конца волоса прямо в таком виде. Но опционально можно добавить к нему с помощью Vector Math > Add (сложение) вектор волоса, чтобы сделать регулировку плавнее ↓ 
Подключаем получившийся результат ко входу Offset нода Set Position, волосы изменяют наклон и увеличиваются в длине ↓
Наша следующая задача - сделать сборку, которая будет возвращать длину волос после этой операции. Напомню, для этого нам нужно вектор получившихся волос умножить на отношение длины волос до изменения наклона к длине волос после изменения наклона.
Для назначения концам волос их новых отредактированных позиций нам понадобится ещё один нод Set Position, а измерить длину вектора волоса мы можем с помощью Vector Math > Length (длина) 
Однако, возникает вопрос, каким образом нам получить длину волос до и длину волос после изменения наклона. Если мы просто продублируем группу Hair Vector, высчитаем длину обоих векторов Hair Vector и разделим с помощью Math > Divide, это не даст результата, и вот почему... ↓
Нужно вспомнить, что в Геометрических Нодах все поля и атрибуты высчитываются для той геометрии, которая приходит в нод со входом Geometry, к которому подключены ноды соответствующих веток. Если посмотреть, к чему будут подключены обе ветки с векторами Hair Vector, то мы увидим, что в обоих случаях будет использована одна и та же геометрия - геометрия, которая приходит во второй нод Set Position, уже после изменения наклона ↓
Нам надо каким-то образом зафиксировать значение длины волос до изменения их наклона. Для этого мы можем создать для каждого сплайна временный атрибут, содержащий нужное нам значение в нужный нам момент. В рамках одной и той же системы Геометрических Нодов это можно сделать при помощи нода Capture Attribute (захват атрибута), включив его в цепь до первого Set Position. Чтобы нод Capture Attribute учитывался при расчётах, и вход, и выход Geometry у него обязательно должны быть подключены к цепочке нодов, которая приходит в Group Output. Тип данных в Capture Attribute оставляем Float (дробное число с плавающей запятой). Здесь я ошибся, и в настройках домена указал Spline, этого делать не надо, домен должен остаться Point. В качестве назначаемого атрибута подключаем Vector Math > Length, измеряющий длину вектора волоса Hair Vector ↓
Теперь мы можем взять выход Attribute из Capture Attribute, к которому подключен Vector Math > Length (длина) и с помощью Math > Divide (деление) разделить его на значение самого Vector Math > Length. При этом для захвата атрибута будет использоваться значение Vector Math > Length до изменения наклона, а при вычисления отношения, в качестве второго значения - значение Vector Math > Length после изменения наклона. Если посмотреть, к каким нодам со входами Geometry оказывается подключен Vector Math > Length, это начнёт обретать смысл ↓
Полученное отношение мы будем использовать в качестве множителя для регулировки размера вектора волоса после изменения угла наклона с помощью Vector Math > Scale. Подключим результат ко входу Position нода Set Position - обратите внимание, что на этот раз мы меняем не сдвиг точек, а их исходные позиции, поэтому используется вход Position, а не Offset. Мы можем видеть, что теперь волосы поменяли наклон, остались нужной длины, но теперь все растут из центра объекта ↓
Это и правильно, потому что мы используем векторы волос, в чистом виде, то есть разницу между концом и началом каждого волоса. Для того, чтобы теперь вернуть их на свои места, нам остаётся только прибавить с помощью Vector Math > Add к получившимся векторам координаты начал волос ↓
Мы их уже получали раньше для использования в нахождении вектора, но почему бы не повторить процесс ещё раз. Для этого нам понадобятся индексы и количество точек в каждом сплайне, и для начала нужно интерполировать домен, чтобы иметь дело со сплайнами, а не с точками. Добавляем нод Interpolate Domain и переводим домен на Spline, а тип данных на Vector, потому что в качестве поля с атрибутами будут использоваться позиции точек ↓
Добавляем нод Field at Index, позволяющий генерировать поле из атрибутов элементов с определёнными индексами. Тип данных выбираем Vector, а в качестве домена выбираем Points, потому что нам нужно поле с позициями точек ↓
Чтобы посчитать индексы начальных точек волос, используем произведение индексов сплайнов и количества точек в каждом сплайне: ноды Index и Spline Length с выходом Point Count
Подключаем их через Math > Multiply (умножение) в качестве поля с индексами в Field at Index
Подключаем в качестве поля значений Position - и волосы возвращаются на свои места, с изменённым наклоном, скорректированной длиной, словом, всё работает! Ура!  ↓
Исправляю сделанную выше ошибку, возвращаю домен для фиксирования значения длины сплайна до изменения ушла наклона на Point
Ещё один нюанс. Поскольку волосы могут быть разной длины, чтобы угол наклона изменялся равномерно для всех волос, перед тем, как использовать этот вектор в расчётах перекрёстных произведений (Cross Product), имеет смысл его нормализовать с помощью Vector Math > Normalize. Эта операция сохраняет направление вектора, но делает его длину равной единице (одному метру, в стандартной метрической системе Блендер). Таким образом, у нас и вектор нормали, и вектор волоса окажутся одинаковой длины, и таким образом все расчеты будут более универсализированными. С практической точки зрения это будет означать более плавное и предсказуемое управление углом наклона ↓
Компактизируем все ноды с помощью Ctrl+H и H, объединим связки по функциям с помощью рамочек (Ctrl+J), подпишем в N-панели, какая рамочка за что отвечает, выделим все нужные ноды для объединения в группу ↓
Нажмём Ctrl+G, чтобы объединить выделенные ноды в группу ↓
Создадим дополнительный вход, подключив значение Scale, регулирующее угол наклона, к свободному выходу в Group Input
Выйдем из группы с помощью Tab, поставим щиток, чтобы предотвратить случайное удаление во время закрытия проекта, назовём группу GN Hair Rotate
Поздравляю! Второй элемент управления готов!
Вы видите эти уроки, благодаря тем, кто забирает их к себе на стенки, рассказывает друзьям, знакомым, в сообществах, пабликах, чатах, коммьюнити. Без такой поддержки развитие проекта было бы невозможным, и я благодарен каждому.
Выражаю большую благодарность спонсорам. Вы даёте мне возможность продолжать, а другим - бесплатно учиться.
avatar
Да уж... я сильно сомневаюсь что самостоятельно повторю сей урок на бис. Векторная математика мне давалась с трудом. Хотя, конечно, если каждый день ей заниматься, все будет проще.
avatar
Денис Соколинский, целиком охватить всё всегда сложно, стоит разбить задачу на части и разобраться с каждой по-отдельности ) Я так-то дирижёрско-хоровой заканчивал, если что, кафедру Музыкальное Образование - какая там математика, четверти в такте посчитать разве что. Но оно постепенно всё укладывается - что такое вектор, как его высчитать, как его перемещать, как менять его размер, направление, как вектора складываются, как умножаются друг с другом - и так постепенно растёт, скажем, личный инструментарий, который можно применять для каких-то практических задач. Уверен, что то, что я здесь показываю, можно решить ещё множеством других способов. Геометрические ноды целиком построены на векторной математике и линейной алгебре, если с ними заниматься, то имеет смысл копать в этом направлении. Вообще всё 3Д на ней построено, просто за кнопками это не всегда видно.
avatar
Andrey Sokolov, иногда даже в 4-х мерном пространстве (для NURBS используется, например).
avatar
Спасибо за уроки. У меня получилось сделать наклон волос относительно поверхности на нужный угол. Но я Очень плохо понимаю как работает Interpolate Domain и Field at Index.(хотелось бы увидеть уроки по GeometryNodes после завершения избушки).
P.S. не знаю почему, но ColorAttribute в Blender3.4 после separate_color выводит не те значения что записаны цветом(т.е. я залил всё 0.6R:0G:0B, а значение на R =  0.319 и чем ближе к 1 тем точнее показания. по этому у меня после separate стоит square_root(из 0.319 получил 0.564) это хоть немного похоже на заданный цвет)
avatar
Влад Ливерко, у меня есть предположение, что это может быть из-за sRGB конверсии цвета. Кстати позже выяснилось, что использование Color Attributes в геонодах на деформируемых мешах нестабильно, особенно когда используется для определения количества дубликатов волос. Вот здесь я в начале видео показываю, как этого можно избежать - https://youtu.be/WZXO91e2XmM

Subscription levels

1-й уровень

$ 2,27 per month
1-й уровень

2-й уровень

$ 5,7 per month
2-й уровень

3-й уровень

$ 11,4 per month
3-й уровень

4-й уровень

$ 22,7 per month
4-й уровень

5-й уровень

$ 57 per month
5-й уровень

Максимальная поддержка

$ 114 per month
Максимальная поддержка
Go up