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 Избушка».

Математика в 3Д ► 18. Lens Flares. Серийный рендер элементов на Пайтон

Поясню, что имеется в виду и для чего это нужно.
О ЧЁМ РЕЧЬ?
Под серийным рендером я имею в виду рендер одного и того же элемента с разными настройками в разные файлы в разных папках. В случае с N-гоном, который мы делали на прошлом уроке, это может быть разное количество углов, разный уровень сглаживания, разное разрешение картинки и т.д. 
ЧТО МЫ БУДЕМ ДЕЛАТЬ?
Мы отрендерим N-гон в 4 разных разрешениях, в вариантах от 5 до 12 углов, с 4 разными уровнями сглаживания углов и 5 разными уровнями сглаживания границ. В общей сложности у нас получится 640 файлов, рассортированных по типам в 164 папки. После написания скрипта выполнение этой задачи будет занимать меньше минуты. 
ДЛЯ ЧЕГО ЭТО НУЖНО?
В процедурном виде такой элемент, как N-гон, требует довольно большой вычислительной мощности, и если использовать его многократно (а его предполагается использовать многократно), то можно быстро упереться в ограничения, особенно если у вас не очень мощный компьютер. Использовать его в виде уже отрендеренной картинки, текстуры намного эффективнее. Это, кстати, касается и других элементов, не требующих анимации.
НО ЗАЧЕМ РЕНДЕРИТЬ ВСЁ???
• Потому что мы можем - при достаточном опыте такой скрипт пишется за несколько минут.
• Потому что выбрать и закинуть в проект готовую картинку быстрее, чем для каждого проекта вспоминать, как настраивается нод-группа, настраивать её, настраивать сцену, камеру, разрешение, делать рендер, сохранять вручную файл, придумывать ему имя. 
• Потому что этот опыт вы можете при небольшой тренировке использовать для любых аналогичных задач, и вероятно он сократит вам часы, а в дальней перспективе и месяцы вашего рабочего времени. Например, это типичнейшая задача для казуального геймдева - отрендерить разные уровни апгрейда для какой-нибудь локации мобильной версии "Удивительной Фермы" во всех возможных вариантах.
Буду очень благодарен всем, кто решит стать платными подписчиками или разово задонатить на означенные цели. Это сильно мотивирует на продолжение.
ПОДГОТОВКА ПРОЕКТА 
↑ Прошлый раз мы остановились на создании нод-группы, которая генерирует Н-гон (мне надоело переключать раскладку, поэтому теперь он будет называться Н-гон) с регулируемым количеством углов, сглаживанием и размытием границ.
↑ Перед нами стоит задача отрендерить его в квадратные картинки, соответственно нам понадобится изменять разрешение сцены. Чтобы не трогать главную сцену, создадим новую. В верхнем меню над окном Outliner рядом с названием сцены нажимаем иконку с двумя листиками, наложенными друг на друга (создание новой сцены) и выбираем Linked Copy. Эта опция создаст сцену с теми же самыми настройками, и все объекты и коллекции главной сцены будут прилинкованы к новой сцене. То есть это будут не копии объектов и коллекций, а те же самые объекты и коллекции. Блендер позволяет использовать одни и те же элементы в разных сценах. В то же время настройки самой сцены мы можем менять.
↑ Для начала, чтобы не путаться, переназовём сцену. Мы будем фактически использовать её для "запечения" текстур, поэтому назовём её Bake.
↑ Изменим разрешение сцены таким образом, чтобы количество пикселей по ширине равнялось количеству пикселей по высоте. В дальнейшем конкретные значения будут меняться, но в зависимости от разрешения сцены, меняется и область рендера в окне камеры. Теперь, объект плэйн занимает только часть экрана, а нам надо, чтобы он занимал собой всё окно камеры 
↑ Если мы изменим настройки камеры, то они изменятся и в оригинальной сцене, поэтому мы продублируем объект камеры, выделив его в Outliner и нажав Shift+D в 3Д вьюпорте.
↑ В сцене может быть единовременно только одна активная камера, и в каждой сцене может быть своя активная камера. Выделяем в Outliner копию камеры, если она ещё не выделена. Она должна быть активной. В 3Д вьюпорте в верхнем меню нажимаем View > Cameras > Set Active Object as Camera. Теперь активной камерой в этой сцене становится вторая камера, и мы можем изменять её настройки, как нам нужно, не опасаясь что-либо поменять в главной сцене.
↑ На случай, если мы впоследствии будем анимировать в главной сцене основную камеру, к которой привязан и плэйн с материалом Н-гона. Привяжем трансформации камеры в этой сцене к камере главной сцены с помощью констрейнта. Во вкладке с констрейнтами объекта добавляем на новую камеру констрейнт Copy Transforms
↑ В качестве таргета, целевого объекта, с которого нужно скопировать трансформации, выбираем камеру из главной сцены. Теперь трансформации объекта-камеры из главной сцены будут управлять трансформациями объекта-камеры из сцены Bake. Под трансформациями объекта имеются в виду не настройки камеры, а Location, Rotation и Scale объекта-камеры.
↑ Переназовём новую камеру Camera Bake, чтобы не путаться.
↑ Переходим в настройки камеры и в графе Focal Length (фокусное расстояние) вбиваем формулу - исходное значение фокусного расстояния камеры из главной сцены (50 мм), умноженное на отношение ширины к высоте рендера в главной сцене, то есть 50 * (1920/1080). Получаем точное новое значение, при котором область окна камеры в сцене с отношением разрешения 1:1 будет занимать столько же пространства, сколько занимает область окна камеры из главной сцены по вышине. Да ладно, камон, это не сложно. Просто вбейте значения, Блендер всё посчитает за вас.
↑ Нам нужно назначить главную папку, в которую будут рендериться файлы. Предварительно создавать её не обязательно: если Блендер во время рендера не найдёт указанную папку на диску, он просто создаст её сам.
Обратите внимание! Если мы ставим в пути рендера в начале строчки два слэша - //, Блендер "на заднем фоне" автоматически подставляет вместо них путь до папки, в которой лежит текущий проект. Таким образом, если в качестве пути рендера вбить //N-Gons, то в папке проекта будет создан элемент под названием N-Gons. Если после этого названия будет стоять слэш, то Блендер будет воспринимать это как папку, если слэша в конце не стоит - то как файл. Проблема в том, что при указании пути в разных операционных системах (Windows, MacOS, Linux) используются слэши, направленные в разные стороны. Я пока намеренно, в ОС Windows, напишу //N-Gons/ с прямым (не обратным) слэшем на конце, чтобы потом показать, к какой ошибке и в какой момент это приведёт.
↑ Далее. Мы будем рендерить в формат PNG. Он не даёт таких жутких артефактов, которые при ближайшем рассмотрении на большой крупности всегда обнаруживаются в JPEG. В то же время, для подобных элементов, которые нужны только в качестве художественного украшения, и их предполагается использовать в большом количестве, не имеет смысл рендерить 32-битный OpenEXR, который, хоть и вмещает существенно больше полутонов, для подобных задач неоправданно много "весит", даже с самыми жёсткими кодеками, вроде Pxr24. Мало того, поскольку мы будем рендерить чёрно-белые изображения, нам нет нужды рендерить картинку в цвете, поэтому в настройках Color мы укажем BW - Black and White, то есть чёрно-белое изображение, которое занимает в три раза меньше места, чем RGB и в 4 - чем RGBA. Оптимизировать - так уж оптимизировать.
↑ С помощью настроек нод-группы увеличим размер элемента таким образом, чтобы он заполнял собой большую часть экрана, оставив небольшие отступы по краям.
ПОДГОТОВКА К НАПИСАНИЮ СКРИПТА
Никогда не надо недооценивать подготовительные этапы. Никогда. Когда вам кажется, что вы уже знаете, как сейчас будете писать код, и у вас чешутся руки немедленно приступить, остановитесь, и пропишите просто обычными словами, что вы будете делать. Время, которое это у вас займёт, не идёт ни в какое сравнение со временем, которое это вам сэкономит во время написания кода.
↑ Разделим одно из окон и откроем Text Editor, там уже открыт скрипт из позапрошлого урока. Нажав над ним в верхнем меню иконку с двумя наложенными друг на друга листочками, создадим новый текст.
↑ Назовём его программистским словом TODO, обозначающим "надо сделать", с индексом 1. Дальше покрутим настройки нод-группы и составим список значений, с которыми мы бы хотели её отрендерить. Составляя списки, лучше сразу писать цифры через запятую. Для дробных значений в качестве разделителя между целой и дробной частью используется точка. В таком виде мы позже сможем использовать эти списки прямо в коде.
• Во многих движках требуется, чтобы разрешение текстур было степенью двойки. Поэтому я хочу отрендерить элементы в разрешениях 256, 512, 1024 и 2048. Вероятно, для каких-то случаев можно было бы добавить и 128, и даже 64 и 32, но после того, как всё будет готово, добавить нужные числа в список и отрендерить недостающие элементы труда не составит.
• По количеству углов - квадраты и треугольники мне вряд ли понадобятся; опять же если что я всегда смогу их потом дорендерить отдельно. А больше 12 углов в маленьком разрешении уже будет не очень хорошо считываться, как углы. Поэтому я ограничусь количеством углов от 5 до 12.
• Сглаживание углов мне нужно в нескольких вариантах - совсем небольшое искажение, чуть больше, сильное и совсем сильное. Сглаживание больше 0.5 уже практически стирает углы
• То же касается и размытия границ - несколько вариантов ближе к нулю нужно для разного разрешения текстур, а вариант с полным размытием - просто, чтобы был, вдруг где-то понадобится.
↑ После составления списка придумываем и прописываем структуру файлов и папок - что где должно лежать, каким образом оно будет рассортировано и как примерно будет называться. Понимаю, насколько организация рабочего пространства может зависеть от индивидуальных предпочтений, потому просто привожу структуру в качестве примера, ни в коем случае её вам не навязывая.
↑ После этого подробно прописываем алгоритм - что, в какой последовательности должно происходить. Часто кажется, что такие вещи можно сразу писать в коде, но иметь под рукой такую памятку, на которую можно ориентироваться во время написания кода, всегда очень, очень полезно и сильно помогает ничего не упустить. Не буду пересказывать, просто откройте скриншот в полный экран и почитайте. Если есть вопросы, задавайте в комментариях.
↑ Далее разделяем Text Editor на две части и в одном из окон создаём новый текст, который уже будет нашим скриптом. В разных окнах можно открывать разные тексты и настраивать им разную крупность, прокручивая колёсико мыши, одновременно удерживая Ctrl на клавиатуре.
↑ Называем новый текст NGon Batch Render (серийный рендер Н-Гона)
НАПИСАНИЕ СКРИПТА
↑ Для начала импортируем в скрипт модуль bpy, с помощью которого в скрипте Пайтон можно управлять Блендером. Это делается с помощью команды import bpy. Зададим на новых строчках константы MATERIAL и GROUP, равные соответственно названиям материала и нода с нод-группой. Обратите внимание на следующие моменты:
• Название нода, который используется в качестве нод-группы, не является названием нод-группы. Если мы зайдём в N-панель, вкладка Node, то увидим, что нод называется Group.015 (у вас индекс может отличаться). Нам нужно именно это название, а не название группы N-Gon, потому что по нему мы позже сможем получить доступ к ноду. Группа N-Gon может использоваться во многих нодах, и у каждого обязательно будет разное название, потому что, как и в случае с объектами, для нодов в рамках одного материала название является уникальным идентификатором и не может повторяться.
• В скрипте все элементы, которые являются текстом - а название материала и нода является текстом - пишутся в кавычках. Это даёт Пайтону понять, что мы присваиваем константам текстовые (строковые) данные, а не некие переменные, которые ему неизвестны.
• В Пайтон, в отличие от С и многих других языков, как таковых констант нет. То есть то, что мы пишем название констант с большой буквы и в начале скрипта, нужно только для нас самих, чтобы мы понимали, что эти данные не подлежат изменению на протяжении выполнения скрипта, и самостоятельно придерживались этой концепции. Функционально они остаются такими же переменными, как и любые другие, это не накладывает на них никаких ограничений, их значения можно в любой момент изменять, это, повторюсь, нужно, только нам самим, чтобы избежать путаницы. 
• Для тех, кто знаком с другими языками программирования и не знаком с Пайтон - в Пайтон динамическая типизация, то есть тип данных переменных определяется не в момент создания переменной, а в момент присвоения ей значения, и может меняться.
↑ Теперь нам нужно "достучаться" непосредственно до материала, с которым мы работаем, а там и до самого нода, чтобы получить доступ к его свойствам. Как и в случае с объектами, который мы рассматривали в уроке про оптимизацию расчётов в шейдерах с помощью пайтон, чтобы получить доступ к материалу, нужно обратиться к классу с функционалам словаря, располагающемуся в подмодуле data модуля bpy, и называющемуся materials - то есть все материалы - и использовать название материала после него в квадратных скобках:
bpy.data.materials[MATERIAL]
Вместо непосредственно названия материала "Lens Flares" мы используем заданную выше константу, в которой это название хранится. Это сделано для того, чтобы если нам понадобится когда-то изменить материал, мы могли это сделать в константе в начале скрипта., а не искать по всему коду, где нужно заменить это название.
Получив доступ к материалу, через точку мы можем обратиться к его свойствам, среди которых есть свойство node_tree (нодовое дерево), у которого в числе его свойств есть класс с функционалом словаря nodes - то есть все ноды. Используя имя нода в квадратных скобках в качестве ключа, мы получаем доступ уже непосредственно к самому ноду и, соответственно, всем его свойствам. Имя нода, как и имя материала ранее, мы берём из другой заданной выше константы:
bpy.data.materials[MATERIAL].node_tree.nodes[GROUP]
Можно выполнить скрипт, нажав кнопку Плэй в верхнем меню Text Editor, либо Alt+P на клавиатуре. Если всё сделано правильно, скрипт отработает без ошибок, и ничего не произойдёт, потому что мы ещё не прописали, что что-то должно происходить.
↑ Однако, если мы где-то всё-таки ошиблись, например, опечатались в названии материала, написав не "Lens Flares", а "Lens FLares" (видите разницу? Пайтон видит), то во время выполнения скрипта он будет остановлен и высветится ошибка.
↑ Подробнее можно прочитать, что произошло, в Верхнем меню > Window > Toggle System Console, куда "сваливаются" все сообщения об ошибках.
🛈 На MacOS и Linux системной консолью является Терминал. Чтобы использовать его в таком ключе, нужно запускать Блендер из-под Терминала, указав полный путь до исполняемого файла в программе Блендер.
↑ В консоли мы можем прочитать, какая ошибка произошла:
'ОшибкаКлюча: ... ключ "Lens FLares" не был найден'
А также путь до файла, при исполнении которого произошла ошибка и строчку в коде, которая привела к ошибке - line 6, то есть 6-я строчка. Строки в Text Editor пронумерованы, поэтому можно легко найти место, которое приводит к ошибке.
Правда, понять, почему оно приводит к ошибке, не всегда бывает так легко. 
↑ Поскольку мы будим исполльзовать один и тот же нод для скрипта, добавим ещё одну константу NODE и сделаем её равной самому ноду с нод-группой, чтобы мы могли в дальнейшем удобно обращаться к его свойствам:
NODE = bpy.data.materials[MATERIAL].node_tree.nodes[GROUP]
↑ Что нам нужно сделать дальше? Видите, как легко можно увлечься и забыть, что мы делали? Именно для этого у нас есть зафиксированный ранее в простой печатной форме алгоритм. Смотрим, что нам нужно задать переменные, фиксирующие текущие настройки сцены. Начнём с разрешения сцены. Создадим переменную scene_resolution. Но как нам получить доступ к параметру, определяющему разрешение сцены. Вот представим, что вы не проходите урок, а пытаетесь разобраться сами. Как вы узнаете, в каком модуле/подмодуле/классе находится нужный параметр?
↑ Крайне полезным может оказаться в этом случае, если у вас в Edit > Preferences > Interface включена галочка Python Tooltips. С ней при наведении на любой параметр высвечивается путь в Пайтон до этого параметра.
↑ Второй способ ещё более прямолинейный. Чтобы не переписывать путь до параметра вручную, можно нажать на параметре правой кнопкой мыши и выбрать пункт Copy Full Data Path. Так вы скопируете в буфер обмена полный путь до нужного вам свойства. Согласитесь, Пайтон в Блендер становится всё более прозрачным и дружелюбным.
↑ Вставляем скопированное свойство в скрипт с помощью Ctrl+V. Если присмотреться к тому, что мы скопировали, и разобрать путь на части, то всё окажется логичным и знакомым. Используется всё тот же подмодуль data модуля bpy. В нём класс с функционалом словаря scenes, очевидно, содержащий все сцены в проекте. После словаря в квадратных скобках следует ключ словаря - названия сцены. После, через точку, мы получаем доступ к свойствам сцены, находящейся в словаре под этим ключом. Среди этих свойств есть свойство render, видимо, отвечающее за настройки рендера, среди которых есть свойство resolution_x, то есть разрешение сцены по Х. Всё читаемо, всё логично.
↑ Единственное, я предлагаю заменить жёсткую привязку к конкретной сцене более мягким вариантом - привязкой к текущей сцене, то есть к сцене, в которой мы в данный момент находимся, которая является активной сценой проекта. Чтобы это сделать, вместо обращения к словарю bpy.data.scenes, мы обратимся к другому подмодулю модуля bpy - а именно, context. В этом подмодуле аккумулируется вся информация о текущем состоянии сцены - о выбранных объектах, об активных материалах, о том, в каком окне в данный момент находится курсор вашей мыши и т.д. В частности, свойство scene подмодуля context вернёт ссылку на текущую выбранную сцену:
bpy.context.scene
и через точку мы точно так же сможем получить доступ ко всем тем же нужным нам её свойствам:
bpy.context.scene.render.resolution_x
↑ Создадим новую переменную scene_output, куда сохраним путь рендера, указанный в настройках. Используем уже проверенный метод - правой кнопкой мыши щёлкаем на строке с указанием пути до папки/файла, выбираем Copy Full Data Path
↑ С помощью Ctrl+V вставляем скопированный путь к данным в скрипт
↑ Так же заменяем жёсткую привязку к конкретной сцене на привязку к текущей сцене, используя bpy.context.scene. Теперь нам нужно зафиксировать исходные настройки нод-группы. Нам нужны только те настройки, которые предполагается изменять в процессе выполнения скрипта. Начнём с количества углов Н-гона. Создадим переменную node_corners и с помощью правой кнопки мыши в Shader Editor скопируем полный путь до нужного нам параметра.
↑ Если мы сейчас вставим его в скрипт, то увидим две вещи: во-первых, длинную-предлинную строку с путём до свойства, а во-вторых, что часть этой строки у нас уже существует выше в виде константы NODE.
↑ Заменяем часть, обозначающую путь до нода (надеюсь, на этом этапе вы уже можете самостоятельно разобраться, какая именно это часть) на константу NODE, и запись становится гораздо компактнее и читаемее.
↑ Создадим переменную node_round и скопируем путь до параметра, отвечающего за скругление углов. Только на этот раз будем использовать не Cope Full Data Path, а просто Copy Data Path, потому что вместо части полного пути до свойства мы всё равно будем использовать константу.
↑ Вставляем скопированное с помощью Ctrl+V в скрипт после константы NODE, через точку. И видим, что путь всё равно длинный, часть .node_tree.nodes["Group.015"] нам нужно удалить, потому что она уже прописана в NODE.
↑ Что мы и делаем. Нам остаётся зафиксировать параметр, отвечающий за размытие границ. Давайте попробуем это сделать на этот раз вручную, уже без копирования пути до свойства через правую кнопку мыши. Если внимательно посмотреть на код, то, даже вообще не зная программирования, можно понять логику. Параметр Corners в ноде обозначался как inputs[1].default_value. Параметр Size мы не прописывали. Параметр Round обозначался как inputs[3].default_value. Как по аналогии достучаться до параметра Smooth?
↑ Ответ - inputs[4].default_value. Inputs - обозначает все входы, а в квадратных скобочках мы пишем индекс нужного параметра. 
🛈 Имейте в виду, что индексы массивов в пайтон начинаются с 0, то есть первым входом в нод, с нулевым индексом, является вход Vector. 
↑ Далее добавим константы RESOLUTION (разрешение текстуры), CORNERS (количество углов), ROUND_LEVELS (уровень скругления углов) и SMOOTH_LEVELS (уровень сглаживания границ). В квадратных скобочках, обозначающих в Пайтон тип данных List (список), пропишем те значения, по которым мы будем в дальнейшем проходиться, выставляя их в качестве значений соответствующих параметров и делая рендер. Просто копируем их из TODO 1 и через знак равно вставляем между квадратных скобок после названий констант. Для числовых значений кавычки не нужны. Напомню, параметры должны идти через запятую, а в дробных числах целая и дробная часть разделяются точкой.
Далее по плану нам нужно для каждого разрешения произвести ряд действий. Чтобы пройтись по списку, выбирая каждый элемент, и проделать с ним всё, что нам нужно, в Пайтон используется синтаксическая форма:
for имя_переменной  in список_элементов:
что в переводе значит для каждого элемента в заданном списке:
Конструкция for - это цикл. Он подставляет значения из списка вместо переменной и проводит с ними все действия, которые мы пропишем, каждый раз возвращаясь к своему началу, пока не пройдётся по всем значениям.
В данном случае список элементов, по которому нужно пройтись - это список значений разрешения сцены RESOLUTIONS. И чтобы по нему пройтись, мы задаём прямо в самой синтаксической форме новую переменную res (от англ. resolution - разрешение). Получается:
for res in RESOLUTIONS:
В конце формы ставится двоеточие, дающее Пайтон понять, что со следующей строки через отступ начинается тело цикла for, которое он должен выполнять на каждом проходе. В процессе выполнения программы вместо переменной res пайтон будет "на заднем фоне" подставлять конкретные значения из списка RESOLUTIONS. То есть будет происходить следующее.
Он возьмёт первое значение из списка RESOLUTIONS, заменит переменную res на это значение и выполнит с ним всё, что прописано в теле цикла. Дойдя до конца тела цикла, обозначенного одинаковыми отступами от начала строки, цикл вернётся к началу своего тела, заменит переменную res на следующее значение 

из списка RESOLUTIONS и выполнит всё, что требуется, уже с этим значением - и так, пока не пройдёт по всем значениям. (Либо пока Пайтон не наткнётся на ошибку или на ключевые слова, которые останавливают выполнение цикла, но их мы сегодня не рассматриваем).
↑ Первым делом, согласно прописанному нами алгоритму, нам нужно сгенерировать путь до папки, в которой будет лежать всё, связанное с текущим разрешением. У нас есть переменная scene_output, содержащая значение параметра Output в настройках рендера, то есть путь до файла, который предполагается отрендерить. Это текстовый формат данных, строка, и мы можем использовать её как основу, но нам нужно вписать после этого пути название нашей новой папки, чтобы Пайтон и Блендер поняли, что это именно путь до папки, а не что-либо ещё. Как это правильно сделать? Дело в том, что, как уже упоминалось раньше, на разных системах пути до папок прописываются через разные слэши. Положим, если вы пишите скрипт только для себя, вы точно знаете, в какой системе вы работаете и какие слэши в ней используются для обозначения перехода в папки, вы, конечно можете просто пробить "жёсткий" путь в виде ещё одной строки и объединить две строки вместе, но это плохая практика. Мало того, что ваш скрипт, вероятно, будет впоследствии использовать кто-то ещё (вдруг вы захотите продавать своё творчество на стоках?) или жизнь повернётся таким образом, что вам придётся перейти на Линукс или, наоборот, с Линукса на Виндовс - и ваш скрипт уже не будет работать, вам придётся искать ошибку, исправлять её. А если плохая практика войдёт в привычку, то вы можете, сами того не заметив, написать очень много скриптов, которые потом вероятно придётся править.
Словом, всегда лучше использовать правильные решения, которые дают гарантированный результат, не требуя от вас даже выяснять, какие слэши используются в вашей системе. То есть в этом случае правильное решение ещё и проще, чем неправильное.
↑ И это решение - использование модуля os, встроенного в Пайтон. В Пайтон есть ряд встроенных модулей, которые поставляются вместе с интерпретатором, и которые можно импортировать без дополнительных предварительных установок. Модуль os - один из них. В блоке импорта пишем:
import os
И теперь мы можем использовать модуль os, предназначенный для работы с операционными системами, и написанный квалифицированными программистами. Абсолютно бесплатно. Имейте в виду, что это никакое не читерство, что большая часть работы с Пайтон строится на использовании встроенных или сторонних модулей, написанных под конкретные задачи. Если вы надумаете заниматься программированием более углублённо, знание модулей и умение с ними работать будет необходимой частью вашего пути.
В модуле os есть подмодуль path, который отвечает за взаимодействие с путями в разных системах. И у подмодуля path есть очень удобный метод join(), позволяющий объединить две части пути в один путь. Обе части - основной путь и, например, название папки, "скармливаются" ему между круглых скобок в виде его параметров, через запятую, и он автоматически выясняет, в какой операционной системе выполняется скрипт и на выходе выдаёт уже правильный сформированный объединённый путь ↓
↑ Итак, мы говорим, что переменная res_folder будет равна
os.path.join(scene_output, res)
То есть в качестве параметров для объединения мы используем переменные scene_output, в которой хранится исходное значение параметра Output (текущего указанного пути рендера), и res, в которую подставляется значение разрешения сцены. То есть по замыслу должен сгенерироваться путь до папки, названием которой будет служить числовое значение разрешения - то есть 256, 512 и т.д. Сама папка не обязана существовать. 
Можно попробовать выполнить скрипт, нажав кнопку Плэй в Text Editor
↑ И мы получаем ошибку! Ура! Гораздо лучше, когда ошибку получаем мы, а не сторонний пользователь, у которого вид даже нескольких строк кода вызывает оторопь. Давайте разбираться, что не так, тем более, что нам уже всё написали, что не так.
↑ Открываем системную консоль (Window > Toggle System Console на Виндовс или Терминал с запущенным из под него Блендер на других системах). Читаем последнюю строчку. Если не знаем английский, пользуемся переводчиком. Там пишут (в моём вольном переводе):
в методе join() аргументы должны быть строкой, байтами, объектом типа os.Path, а не int
Всё перечисленное - это типы данных в Пайтон. str - это текстовый формат или строка, bytes - это байты, os.Path - это объект класса Path из модуля os, а int - это integer, целочисленное значение, если по-русски. Выше указываются строки и пути до файлов, выполнение которых привело к ошибке. Находим наш файл, и видим, что ошибка произошла в 20-й строке, когда мы вызывали os.path.join().
Из всего этого можно, понять, что ему не нравится, что мы пытаемся "скормить" методу join() не тот тип данных. Он самостоятельно не может преобразовать целочисленное значение в приемлемый для выполнения формат. Что за целочисленное значение? Так мы же ему скармливаем в переменной res разрешение сцены, а это число, а не текст.
↑ Чтобы помочь ему, мы можем вручную преобразовать целочисленное значение в строку, используя встроенную в Пайтон функцию str(). Просто "скармливаем" ей в качестве параметра переменную res, и что бы в ней не приходило, Пайтон попытается преобразовать это в строку, которая теперь будет использована в методе join() в качестве названия добавляемой папки
↑ Чтобы не работать вслепую, мы можем использовать встроенную в Пайтон функцию print(), которая распечатывает в системную консоль то, что мы ей "скармливаем" в качестве параметра. Скормим ей переменную res_folder, в которой должен содержаться правильный путь до папки с нужным разрешением, выполним скрипт и посмотрим, что распечаталось в консоли
↑ И здесь мы можем отследить ту ошибку, о которой я говорил во время подготовки проекта. Она не связана с самим скриптом, она связана с тем исходным путём, который я указал в Output. Помните, я поставил слэш после названия папки N-Gons, чтобы обозначить, что это название папки, а не название файла? Так вот, сейчас я могу видеть, что я поставил не тот слэш. os.path.join() определил систему и при объединении поставил правильный слэш. Так получилось, что в пути оказалось два слэша /\
↑ Повторюсь, дело здесь не в скрипте, а в том, что не надо заниматься самодеятельностью. Просто убираю последний слэш в графе Output в настройках рендера и запускаю скрипт ещё раз.
↑ Ну вот, другое дело. Теперь генерируется путь до папки, который нам нужен. Точнее, пути до четырёх папок с разными разрешениями, которые нам нужны. Впоследствии мы их будем подставлять в качестве значения Output. Но сначала их нужно сформировать полностью, потому что в этих папках будут лежать ещё другие папки. 
↑ А также, по плану алгоритма, нам надо для каждого разрешения из списка выставить его в качестве разрешения сцены. Мы уже выше выясняли, что путь до разрешения сцены записывается как bpy.context.scene.render.resolution_x - это разрешение сцены по оси Х. Печатаем, что оно будет равняться переменной res. То есть на каждой итерации цикла оно будет меняться.
↑ Как можно заметить, мы часто обращаемся к свойствам рендера сцены, поэтому, вероятно, стоит их записать в отдельную переменную, которую мы будем использовать вместо длинного пути, для читаемости и удобства восприятия. Создадим новую переменную sc_render, которая будет ссылаться на bpy.context.scene.render
↑ Теперь можно использовать её, вместо того, чтобы каждый раз прописывать весь длинный путь целиком: sc_render.resolution_x гораздо читаемее, чем bpy.context.scene.render.resolution_x, не так ли?
↑ В Пайтон мы можем присваивать нескольким параметрам одно и то же значение прямо в одной строке. Мы пишем, что разрешение сцены по Х будет равно разрешению сцены по Y, которое будет равно переменной res:
sc_render.resolution_x = sc_render.resolution_y = res
↑ И давайте сразу же, пока мы не забыли в конце кода напишем, чтобы после того, как всё, что мы задумаем, выполнится, разрешение сцены вернулось к исходному значению, которое мы сохраняли в переменной scene_resolution. Для этого уже после цикла for, без отступа пишем:
sc_render.resolution_x = sc_render.resolution_y = scene_resolution
↑ Пока мы не наворотили всего ещё больше, давайте выполним скрипт и проверим, что разрешение сцены после его выполнения остаётся прежним
↑ А чтобы убедиться, что оно всё-таки меняется в процессе, пропишем print(), в котором будем считываться уже не переменная, которую мы сами генерировали, как раньше, а непосредственно текущее значение нужно нам параметра, а именно:
print(sc_render.resolution_x)
↑ Выполняем скрипт, смотрим в консоли - всё выполняется правильно, на разных итерациях значение разрешения в сцене разное.
↑ Далее для каждого разрешения в сцене нам надо пройтись по списку с количеством углов. Это можно сделать при помощи вложенного цикла for - когда  в одном цикле на каждой итерации срабатывает другой цикл. В теле первого цикла, с теми же отступами, пишем:
for corner in CORNERS:
Всё то же самое, только для этого цикла переменная, в которую будут подставляться значения количества углов, будет называться не res, а corner. И первое, что нам нужно сделать - сгенерировать путь до папки. Для этого мы используем всё тот же метод os.path.join(), только теперь вместо str(res) пишем str(corner). И если с разрешением всё было понятно, то мне бы хотелось, чтобы папки с количеством углов назывались не просто цифрами - а то как я потом вспомню, что это за цифры и что они обозначают?  - а чтобы после цифры, обозначающей количество углов в названии папки шло слово corners. Добавить к тому, что нам будет выдавать str(corner), слово "corners", можно при помощи знака +. Сложение строковых данных рассматривается Пайтон, как их объединение. Например если мы напишем "Hello" + " Blender", то на выходе получится "Hello Blender". Также и здесь, мы пишем в качестве второго аргумента, обозначающего название папки:
str(corner) + " corners"
Всю эту длинную формулировку Пайтон воспринимает как единое строковое значение. Итого получается:
corner_folder = os.path.join(scene_output, str(corner) + " corners")
По замыслу в папке с каждым разрешением должен лежать набор папок с разным количеством углов, но сейчас в этой строке присутствует неточность. Попытайтесь найти её сами и понять, как её исправить. А позже я покажу, в чём она заключается.
↑ Добавим print(), который будет распечатывать в консоль пути до папок с разным количеством углов:
print(corner_folder)
↑ Выполним скрипт и увидим, что папок стало уже намного больше. Опять же, если вы ещё не нашли неточность, то здесь, если внимательно вглядеться в пути до папок, она становится более очевидной. Но исправим мы её позже.
↑ А пока нам нужно для каждого значения из списка углов назначить его в качестве количества углов в ноде. Как достучаться до нужного параметра нода, мы уже выяснили, остаётся только переключить его на переменную corner на каждой итерации:
NODE.inputs[1].default_value = corner
↑ И, опять же, чтобы после выполнения скрипта все параметры нода вернулись к исходным значениям, после цикла for восстанавливаем значение параметра из сохранённой ранее переменной:
NODE.inputs[1].default_value = node_corners
↑ Теперь по плану для каждого количества углов в каждом разрешении сцены нам надо пройтись по каждому заданному уровню сглаживания. Мы сделаем это в ещё одном вложенном цикле for. И первое, что нам нужно будет там сделать - это сгенерировать путь до папки:
for rlevel in ROUND_LEVELS:
    rlevel_folder = os.path.join(...)
...и вот сейчас самое время исправить неточность. Для генерации путей до папок с разным количеством углов нам следовало использовать в качестве первого аргумента в функции не scene_output (то есть корневую папку, в которой лежат все папки), а res_folder (то есть путь до папки с разным разрешением, сформированный в цикле for верхнего уровня), чтобы в каждой папке с разрешением лежал свой набор папок с разным количеством углов:
corner_folder = os.path.join(res_folder, str(corner) + " corners")
↑ И следуя той же логике, для генерации пути на уровне rlevel мы в качестве основного пути будем использовать так же не scene_output и даже не res_folder, а уже corner_folder из предыдущего уровня вложенности, чтобы в каждой папке с разными разрешениями лежал свой набор папок с разным количеством углов, а в каждой папке с разным количеством углов лежал свой набор папок с разным уровнем сглаживания углов:
rlevel_folder = os.path.join(corner_folder, str(corner_folder, str(rlevel)+" round")
Добавляем к названию папки, состоящему из цифры с уровнем скругления, с помощью плюса строку " round", чтобы, опять же, понимать, что лежит в этих папках.
↑ Для каждого уровня скругления нам нужно выставить соответствующее значение в ноде. Всё то же самое, только в качестве применяемого значения используется уже переменная rlevel, вместо которой на каждой итерации подставляются соответствующие значения из ROUND_LEVELS:
NODE.inputs[3].default_value = rlevel
↑ Не забываем в конце восстановить значение до исходного, сохранённого в переменную node_round:
NODE.inputs[3].default_value = node_round
↑ И для каждого уровня скругления мы должны, наконец-то пройтись по всем уровням размытия краёв, в очередном вложенном цикле for с новой переменной slevel, в которую будут подставляться значения из SMOOTH_LEVELS:
for slevel in SMOOTH_LEVELS:
В коде сейчас допущена опечатка, исправлю позже. А сейчас мы уже можем сформировать название файлов, которые будут лежать в соответствующих папках.
↑ Сначала мы сформируем само название файла, а потом с помощью os.path.join() объединим его с длинным путём по всем папкам до этого файла. Поскольку мы будем рендерить Н-гоны, начало названия у всех файлов будет "Ngon", его прописываем вручную. Если хотите, можно задать его в начале скрипта в качестве константы, и использовать уже константу, чтобы потом не искать по всему скрипту, где формируется название.
Обратите внимание на букву f перед строкой "Ngon", она стоит там не случайно. В поздних версиях Пайтон это знак форматируемой строки (раньше это реализовывалось по-другому, но так намного нагляднее и удобнее). Ф-строки позволяют автоматически подставлять в строку значения переменных, которые нужно указывать в строке фигурных скобках на тех местах, где они должны располагаться:
↑ На нижних уровнях вложенных циклов мы можем использовать переменные с верхних уровней. Мне хотелось бы, чтобы в названии файла в краткой форме была записана вся информация о его содержании - разрешение, количество углов, кровень скругления и уровень сглаживания. Сначала в фигурных скобках провожу переменную res, которая на верхнем уровне циклов определяет разрешение картинки:
f"Ngon_{res}"
С помощью нижнего подчёркивания отделяю части названия файла друг от друга. В таком виде, если переменная res, например, будет равна 256, результат Ф-строки будет "Ngon_256".
↑ Далее по тому же принципу добавляем в отдельных фигурных скобках переменную corner, а после неё вне фигурных скобок я ставлю букву с - она даст понять, что эта цифра относится к corners, то есть к количеству углов, и при этом не сильно удлинит название файла:
f"Ngon_{res}_{corners}c"
При разрешении 256 и количестве углов 5 название будет "Ngon_256_5c" - по логике вполне угадываемо и при этом читаемо.
↑ Добавляем значение скругления rlevel и суффикс rnd (от round):
f"Ngon_{res}_{corners}c_{rlevel}rnd"
C разрешением 256, 5-ю углами и сглаживанием 0.15, название будет "Ngon_256_5c_0.15rnd"
↑ И, наконец, добавляем значения сглаживания границ slevel с суффиксом smth:
f"Ngon_{res}_{corners}c_{rlevel}rnd_{slevel}smth"
Полное название будет выглядеть примерно так:
Ngon_256_5c_0.15rnd_0.02smth.png
Расширение файла Блендер подставляет автоматически, в зависимости от формата, его не нужно прописывать в коде. Длинновато, конечно, и, может, чересчур технически, но если знать, куда смотреть, то по названию можно легко понять, что за картинка содержится в файле.
Разумеется, вы вольны придумывать и формировать нейминг по своему усмотрению, это всего лишь пример.
↑ Чтобы просмотреть названия файлов, добавим print(file_name)
↑  В этом месте мне нужно исправить опечатку в обращении к константе SMOOTH_LEVELS, которое я в 30-й строке написал без подчёркивания, что привело к ошибке.
↑ Опечатка исправлена, запускаем скрипт ещё раз
↑ Смотрим, сколько файлов нам предстоит отрендерить. Нервно сглатываем при мысли, что это пришлось бы делать вручную. 
↑ Теперь нам нужно объединить путь до файла через все папки с названием файла. Делаем это всё так же с помощью os.path.join() - ему всё равно, объединяем мы папки или файлы, он объединит всё, что мы скажем, главное, чтобы оно было в строчном формате. Результат сохраняем в новую переменную filepath:
filepath = os.path.join(corner_folder, file_name)
И здесь я в очередной раз перепутал переменную, и вместо rlevel_folder с предыдущего уровня использовал corner_folder с пред-предыдущего. Ничего страшного, в дальнейшем вы увидите, насколько просто такие вещи исправляются, когда мы имеем дело с программированием.
↑ По размышлении я пришёл к выводу, что в дополнительной переменной filepath нет нужды, и можем напрямую назначать путь до файла сразу в sc_render.filepath - то есть в качестве параметра Output активной сцены. Помните переменную sc_render, ссылающуюся на настройки рендера сцены, которую мы создавали раньше?
↑ Также для каждого уровня размытия нам надо выставить его в качестве параметра в ноде:
NODE.inputs[4].default_value = slevel
↑ И не забыть восстановить исходные значения из сохранённых переменных после выполнения всех циклов for:
NODE.inputs[4].default_value = node_smooth
↑ Нам останется только на каждой итерации нижнего уровня запустить рендер. Откроем верхнее меню, вкладку Render и посмотрим, с помощью какой команды это делается.
↑ Нажимаем правой кнопкой мыши на кнопке Render Image и выбираем Copy Python Command
↑ Прежде, чем мы используем эту команду в скрипте, давайте с помощью Ctrl+V вставим её в Python Console - это один из типов окна в Блендер, не путать с системной консолью - и с помощью автозаполнения посмотрим, с каким параметрами её можно запускать.
↑ Удаляем с конца всё, вплоть до открывающейся скобки и нажимаем Tab 
↑ Видим описание, а также полный вызов этого оператора, со всеми значениями параметров, которые он использует по умолчанию. Выделяем в консоли эту строчку полностью и копируем с помощью Ctrl+V
↑ Вставляем в скрипт, она получается очень длинной
↑ В Пайтон если в каком-либо месте кода был открыт любой тип скобок - круглые, прямоугольные, фигурные - дальше интерпретатором всё воспринимается, как содержимое этих скобок, пока скобки не закроются. Не зависимо от переноса строк. Поэтому мы можем перенести все аргументы после скобки на отдельную строку.
↑ Если во время определения функции прописать для её параметров, чему они будут равны по умолчанию. Тогда при вызове функции можно эти параметры не задавать и не указывать, вместо них будут использованы значения по умолчанию. Или можно задавать только те параметры, которые нужно изменить. Поэтому мы, во-первых, ради читаемости каждый параметр перенесём на отдельную строку, а во-вторых удалим лишние - название сцены и слоя, поскольку мы используем текущие сцену и слой, и оставим только три параметра:
animation = False (рендер анимации не используется, то есть рендерится статичная картинка)
write_still = True (статичная картинка после рендера автоматически сохраняется в виде файла на диск по указанному в Output пути)
use_viewport = False (потому что с таким параметром по умолчанию работает кнопка Render Image)
↑ Также по своему опыту могу сказать, что после изменения настроек сцены они не всегда изменяются мгновенно, в отдельных случаях программе требуется отклик от пользователя, чтобы параметр поменялся - чтобы хотя бы начала двигаться мышка по экрану. Но во время выполнения скрипта интерфейс полностью блокируется, как когда Блендер виснет, и отклики от пользователя не считываются, поэтому может сложится ситуация, что даже если вы будете яростно возить мышкой по столу, пока Блендер выполняет скрипт (это ни на что не повлияет), параметры хоть и будут меняться, но Блендер не будет обновлять сцену в связи с их изменением. Вычислить, в каких случаях это происходит, а в каких нет, бывает очень сложно, но можно заставить Блендер принудительно обновить текущую сцену при помощи команды bpy.context.scene.update_tag(), которую стоит расположить когда уже выполнены изменения всех настроек, непосредственно перед вызовом оператора рендера.
↑ Итак, вроде бы всё готово к запуску скрипта, который нам должен будет отрендерить все файлы и сохранить их в нужные папки. Но, опять же, по моему опыту - не забудьте засэйвиться перед этим! Даже если вам кажется, что вы учли всё, и всё должно сработать, как вы задумали, в девяноста девяти процентах случаев, особенно если вы только начинаете заниматься программированием, выяснится, что где-то были допущены неточности, опечатки, ошибки, которые могут привести к зависанию и даже вылету Блендер и утере всего вашего кода или существенной его части. Поэтому сначала сохраняем файл, и только потом нажимаем плэй.
↑ А перед этим, поскольку, когда скрипт запустится, вы уже не сможете его остановить, проверьте настройки рендера, чтобы случайно не оказалось, что вы собираетесь рендерить несколько тысяч файлов на 4096 сэмплах в Cycles в течение ближайшей недели. (Разумеется, всегда можно грохнуть процесс через диспетчер задач). Я буду рендерить в Eevee на 64 сэмплах, для материалов, в которых используется только шейдер типа Emission (или прямое подключение текстур к выходу, что по сути то же самое) этого вполне достаточно.
↑ Нажимаем выполнить скрипт. Если ошибок не появляется, а Блендер подвисает, значит он выполняет задачу. У меня это заняло около минуты.
↑ Если до начала выполнения скрипта у вас была открыта системная консоль, вы можете в ней в реальном времени посмотреть, как Блендер сохраняет ваши файлы в ваши папки
↑ Если ошибок не появилось, у вас должна в папке проекта создаться папка N-Gons, в которой в 36 вложенных папках лежат 640 отрендеренных файлов
↑ Откроем одну из папок. Здесь выясняется, что из-за использования неправильной переменной не создался один уровень вложенных папок - для разных уровней размытия краёв. И что вы думаете, мы будем вручную всё пересортировывать? Пфф, да как бы не так. У нас (по крайней мере, у меня) рендер 640 файлов занимает меньше минуты. Мы просто удалим всю созданную папку целиком, внесём изменения в код и отрендерим всё ещё раз. Даже если оно рендерилось не одну минуту, а десять, это будет быстрее, чем сортировать всё. К тому же там есть ещё кое-что, что хотелось бы исправить.
А именно - цветовое пространство. Обратите внимание, что все элементы на картинках получились серыми. По умолчанию в Блендер включено цветовое пространство Filmic, у которого более широкий цветовой диапазон, позволяющий работать с цветами, значение яркости которых превышают единицу. А у ваших мониторов и вообще любых мониторов цветовое пространство стандартное sRGB, и при прямой конвертации Filmic в стандартный sRGB то, что в Filmic считается белым цветом, в стандартном sRGB становится серым.
Но будем последовательны.
↑ Сначала заменим в пути до файла переменную corner_folder на rlevel_folder, чтобы файлы сохранялись не в папки с разными углами, а в папки с разным уровнем скругления, лежащие в папках с разными углами 
↑ А затем изменим цветовое пространство в настройках рендера во вкладке Color Management на Standard. Нгон сразу же становится намного ярче. На самом деле, просто в стандартном пространстве все цвета с яркостью между 0 и 1 растягиваются на весь доступный диапазон, а всё, что выше 1, обрезается. Но в нашем Нгоне яркость выше единицы конструкцией не предусмотрена. 
↑ Сэйвимся, снова запускаем скрипт, ждём, наблюдаем за консолью (по желанию).
↑ Выясняется, что всё снова рендерится не туда, что у нас откуда-то в папке с разрешением 2048 в подпапке 12 corners взялась папка Ngon_2048_12c_0.5rnd_1smth, и всё почему-то лежит в ней. Если мы вернёмся в настройки сцены, и посмотрим, что сейчас вписано в Output, то там будет эта самая длиннющая строка с адресом, только с каждым запуском она будет всё удлиняться и удлиняться
↑ А всё потому, что нужно было прописать в конце скрипта восстановление пути в Output до сохранённого значения. Кто сам заметил - молодец:
bpy.context.scene.render.filepath = scene_output
А можно и короче, с использованием переменной:
sc_render.filepath = scene_output
Восстанавливаем вручную исходное значение Output - //Ngons - удаляем корневую папку с рендерами, запускаем скрипт.
↑ На этот раз всё получилось. Все файлы отрендерены, поименованы и рассортированы по папкам. Однако, при ближайшем рассмотрении закралась ещё одна проблема - на отдельных рендерах с определёнными настройками элемент выходит за границы рендера
↑ Но это решается уже без скрипта, просто в настройках нод-группы уменьшаем на проблемном элементе параметр Size, пока он не будет умещаться в пределах окна рендера. Этот параметр мы во время выполнения скрипта не меняем, поэтому он изменится для всех картинок. Но то, что остальные картинки станут чуть меньше, не страшно, они есть в разных разрешениях, и вариантов выбора там с большим запасом 
↑ Ещё раз перезапускаем скрипт. Можно даже не удалять папку - если структура папок и файлов не менялась, Блендер просто перезапишет уже существующие файлы. Обожаю его за отсутствие надоедливых "Файл существует, хотите его перезаписать?"
↑ Бегло пролистываем файлы, убеждаемся, что всё в порядке
ИСПОЛЬЗОВАНИЕ В МАТЕРИАЛЕ
Файлы картинок двухмерные, по умолчанию Image Texture распределяет текстуру по координатам UV, и чтобы использовать её с координатами Object, нам придётся произвести небольшую коррекцию.
↑ Но для начала просто выберем и загрузим картинку в Image Texture. Я выбрал разрешение 512 (с запасом, хватило бы и 256), восьмиугольный Нгон с небольшим скруглением на уровне 0.15 и небольшим размытием краёв на уровне 0.05. Я знаю это, потому что это прописано прямо в названии файла.
↑ Для правильного распределения градиента переводим Image Texture в режим Non-Color, чтобы использовались линейные значения яркости, без корректирующего коэффициента, который накладывает sRGB.
↑ Чтобы добавить элемент к остальным элементам используем Math > Add (сложение), чтобы контролировать яркость элемента - Math > Multiply (умножение). К Image Texture сейчас подключены те же координаты, что и к остальным элементам... И так много элементов нам не нужно, нам пока нужен один.
↑ Переводим режим Image Texture с Repeat на Clip, чтобы убрать повторы 
↑ Добавляем к текстурным координатам по осям X и Y значение 0.5 с помощью Vector Math > Add, чтобы сместить элемент в центр системы координат. На скриншоте я добавляю ещё и по Z, но это неправильно, Z мы не трогаем, оно остаётся на нуле.
Чтобы регулировать размер элемента, можно использовать Vector Math > Scale, а смещать его с помощью того же  Vector Math > Add
↑ Повторяем, пока не понравится. В дальнейшем нам предстоит автоматизировать добавление этих элементов, а также добавить автоматическую контролируемую рандомизацию их размера и положения по заданной оси.
Поскольку ваша активность достигла исторического минимума, продолжение серии будет только после того, как этот пост в ВК наберёт 500 лайков. Если у вас нет доступа в ВКонтакте или вы не используете его по любым другим причинам, напишите коммент под уроком о вашем желании продолжать обучение, я учту ваш голос при подсчёте.
Рассказывайте о серии своим друзьям, в тематических сообществах и пабликах, всё в ваших руках.
UPD. Лайки собраны! Большое спасибо! Серия продолжается!
avatar
"А, чё, так можно было штоли?" (с) Хороший, урок, премного благодарен за то что выкладываете в свободный доступ много достойной информации. Народ не хочет залезать в питона? Хех)) Я когда-то писал слабенькие игры на Delphi + GLScene, и после того печального опыта зарёкся вообще программировать - не моё это. Тут, пока на улицу носа сунуть нельзя, решил приобрести навык 3D-эшника, а оказывается и тут нужно кодить, только результат нихрена уже не real-time. Картинки на виртуальном серваке гугла приходится рендерить
avatar
Axel Bond, ну кстати конкретно этот эффект, особенно после оптимизации, практически в риал-тайм работает. Или в смысле вообще совсем нет возможности рендерить? Пайтон - один из самых простых, если не самый простой для начального освоения язык программирования. Я сам много лет упирался, а потом в какой-то момент перестал - и Блендер за несколько месяцев изменился для меня полностью, мышление поменялось, я начал применять его чуть ли не в каждом проекте. Как и любому неофиту, мне сложно после такого озарения не пытаться нести свой опыт в массы )
avatar
Andrey Sokolov, конкретно этот эффект - да, а вот мне пришлось рендерить видео на конкурс от NooBlender, так один мой кадр на CPU 2,9x2 ГГц будет рассчитываться несколько часов, а гугловская NVIDIA Tesla T4 выплюнула их мне через три часа 150 штук 512 сэмплов + Denoising. Хотя мне кажется это всё равно долго, я думал владельцы топовых видеокарт вообще сильно не запариваются, ан-нет. Ну да ладно, ждём-с новых уроков
avatar
Доброго времени суток, Андрей! Не оставляйте свои уроки. Такой крутой подачи информации я не видел про блендер. Сегодня мало народа, завтра много будет. Эта информация не на один день. При изучении меняется образ мышления и работы с программой, от - а куда тут нажимать, до - а что, так можно было? Не оставляйте свои труды, они не беЗплодны!
avatar
Супер! Присоединяюсь! Жду новых уроков!
avatar
Tatyana Rzhaksinskaya, спасибо!!!
avatar
Спасибо за крутой урок и качественную подачу материала!
avatar
Спасибо. Невероятно интересно
avatar
Это было сложно, но я справился

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