Добро пожаловать во вторую часть данной серии уроков. Пришло время заняться математикой и научиться контролировать положение, вращение и масштаб мешей.
Этот урок основывается на первой части. Если вы слишком часто теряетесь, попробуйте вернуться и пройти сначала первую часть. Сегодня мы быстро рассмотрим создание кубиков, а затем перейдем к управлению мешами и центрами объектов. В конце урока, мы погрузимся в матрицы преобразования и научимся делать эти самые преобразования быстро и красиво.
Обычная настройка
Давайте начнем с импорта пакетов, которые нам нужны. Помимо обычного bpy, мы также будем использовать функцию radians() из математического пакета Python, а также класс Matrix из mathutils (это из Blender).
import bpy import math from mathutils import Matrix |
Как и ранее, у меня будет раздел для размещения переменных, раздел служебных функций и, наконец, раздел основной части кода. Функция vert() выглядит бесполезной, но она понадобится чуть позже.
# ----------------------------------------------------------------------------- # Settings name = 'Cubert' # ----------------------------------------------------------------------------- # Utility Functions def vert(x,y,z): """ Make a vertex """ return (x, y, z) # ----------------------------------------------------------------------------- # Cube Code verts = [] faces = [] # ----------------------------------------------------------------------------- # Add Object to Scene mesh = bpy.data.meshes.new(name) mesh.from_pydata(verts, [], faces) obj = bpy.data.objects.new(name, mesh) bpy.context.scene.objects.link(obj) bpy.context.scene.objects.active = obj obj.select = True |
Создание куба
Создание куба менее гламурно, чем вы могли ожидать, потому что нет никакого фантастического алгоритма для их создания. Вместо этого мы должны вводить позиции вершин и граней вручную. К счастью, кубы имеют только 6 граней и 8 вершин.
verts = [vert(1.0, 1.0, -1.0), vert(1.0, -1.0, -1.0), vert(-1.0, -1.0, -1.0), vert(-1.0, 1.0, -1.0), vert(1.0, 1.0, 1.0), vert(1.0, -1.0, 1.0), vert(-1.0, -1.0, 1.0), vert(-1.0, 1.0, 1.0)] faces = [(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)] |
Запустите скрипт сейчас, и вы получите стандартный куб. Это было легко! Давайте посмотрим, что с этим можно сделать.
Перемещение центра объекта
Каждый объект в Blender имеет свой центр, который определяет его положение в трехмерном пространстве. Когда мы говорим о позиции объекта, мы фактически говорим о позиции его центральной точки (origin point). С другой стороны, когда мы говорим о центральной точке, мы фактически говорим о центральной точке относительно меша.
Запутались? Посмотрите на изображение ниже.
Как вы можете видеть, когда оранжевая точка (центр объекта) находится на сетке, позиция объекта (0, 0, 0), хотя сам меш поднят на 1 единицу по оси Z и его позиция находится в положении (0, 0, 1). Но интерес заключается в позиции меша. В обоих случаях меш смещен на 1, но во втором случае (если быть точным) он смещен на -1. Если бы позиция меша была (0, 0, 0), он был бы в воздухе, в том же месте, где и его центр объекта. Как вы можете видеть, позиция меша соотносится с положением центра объекта, но не зависит от положения начала оси координат.
Зная это, мы можем сделать две вещи:
- Изменить положение меша относительно центральной точки. Это то же самое, что перемещение меша в режиме редактирования.
- Изменить положение центральной точки, оставив меш в том же месте. В этом уроке это будет центр оси координат.
Мы начнем с изменения положения меша относительно его центра. Я буду называть это смещением, чтобы не запутаться.
offset = (0, 0, 1) def vert(x,y,z): """ Make a vertex """ return (x + offset[0], y + offset[1], z + offset[2]) |
Мы можем использовать функцию vert для перемещения меша с простым добавлением.
Смещение на 1 по оси Z разместит куб на сетке оставив его центр внизу.
Теперь давайте попробуем переместить его центральную точку, при этом оставив куб в центре сцены. Есть только одна небольшая проблема: мы не можем перенести исключительно сам центр объекта. Перемещение центра объект переместит и сам объект в пространстве.
Нам нужно изменить исходное положение центра, тем самым переместив положение меша, а затем переместить меш в обратном направлении относительно смещение центра.
obj.location = [i * -1 for i in offset] |
Помните, что расположение — это кортеж, поэтому мы должны использовать выражение для его установки.
Попробуйте запустить код сейчас. Меш снова находится в центре сцены. Но центральная точка (и объект) теперь находится в положении (0, 0, -1).
Поскольку мы меняем местоположение используя отрицательные значения смещения, результирующая позиция объекта будет отрицательна. Соответственно, использование отрицательного смещения приводит к положительному положению объекта.
offset = (0, 0, -5) |
Итак, что, если вы хотите изменить положение центра объекта, а также изменить позицию меша произвольным образом, чтобы он не находились всегда в центре сцены? Мы можем добавить другое смещение, чтобы представить это. И тогда мы можем добавить это смещение в выражении местоположения, чтобы переместить меш. Давайте также переименуем предыдущее смещение, чтобы все было ясно и понятно.
origin_offset = (0, 0, -5) mesh_offset = (1, 0, 0) def vert(x,y,z): """ Make a vertex """ return (x + origin_offset[0], y + origin_offset[1], z + origin_offset[2]) obj.location = [(i * -1) + mesh_offset[j] for j, i in enumerate(origin_offset)] |
Обратите внимание, что теперь мы должны использовать enumerate(), чтобы получить индекс для добавления.
Есть несколько других вещей, которые мы могли бы сделать с выражениями и функцией vert(). Но есть другой способ преобразования мешей и объектов. Способ, который является более чистым и быстрым.
Войдите в матрицу
Матрицы
В математике матрицы представляют из себя прямоугольные массивы чисел (а иногда и других вещей). Их можно добавлять, вычитать и умножать между собой. Вы найдете матрицы почти в каждой сфере, где задействована математика.
Единственная сфера, которая нас беспокоит, это компьютерная графика, и в этом контексте матрицы часто используются для представления преобразований. Сюда относятся такие вещи, как перемещение, масштабирование или вращение. Матрицы, представляющие линейные преобразования, называются «Матрицы преобразования».
Положение объектов, поворот и масштаб определяются как матрицы преобразования по отношению к системе координат. Даже если вы думаете, что нет никаких трансформации!
Например, давайте представим себе «объект по умолчанию». Этот объект находится в центре оси координат (0, 0, 0), имеет масштаб 1 и поворот 0 (по всем осям). Мы можем представлять местоположение, масштаб или поворот любого объекта в качестве матрицы преобразования этого объекта по умолчанию. В Blender это называется Всемирной матрицей, и это свойство доступно для всех объектов.
Кстати, «координаты сцены» на самом деле называются «World Coordinates» в Blender. Существуют и другие координатные пространства, а также матрицы для них, но мы рассмотрим это в другом уроке данной серии уроков.
Обратите внимание, что вам не нужно быть мастером матрицы, чтобы использовать их. Разработчики blender благословили нас классом Matrix, который делает почти всю работу за нас, и вам даже не придется видеть матрицы, пока вы работаете с ними.
Хорошо, давайте поиграем с этой матричной концепцией. Добавляем новый объект (используя Shift + A). Выберите его, затем вставьте этот скрипт в текстовый редактор и запустите его.
import bpy print('-' * 80) print('World Matrix \n', bpy.context.object.matrix_world) |
Вы должны увидеть примерно следующее в консоли Blender:
-------------------------------------------------------------------------------- World Matrix <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) (0.0000, 1.0000, 0.0000, 0.0000) (0.0000, 0.0000, 1.0000, 0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> |
Поскольку мы не перемещали, не вращали и ничего не делали с объектом, его значения совпадали с нашим воображаемым «объектом по умолчанию». Матрица, подобная этой, называется Матрицей Идентичности в математике, и она означает, что нет никаких трансформаций.
Теперь переместите объект куда-нибудь и снова запустите сценарий.
-------------------------------------------------------------------------------- World Matrix <Matrix 4x4 (1.0000, 0.0000, 0.0000, -8.8360) (0.0000, 1.0000, 0.0000, -1.1350) (0.0000, 0.0000, 1.0000, 8.9390) (0.0000, 0.0000, 0.0000, 1.0000)> |
Ага! Теперь матрица содержит некоторое изменение. Значения будут разными в зависимости от того, куда вы переместите ваш объект. Как видите, последний столбец содержит координаты X, Y, Z по отношению к центру сцены (мировое пространство).
Что будет, если мы сбросим местоположение (Alt + G) и попробуем масштабирование?
-------------------------------------------------------------------------------- World Matrix <Matrix 4x4 (0.7280, 0.0000, 0.0000, 0.0000) (0.0000, -1.4031, 0.0000, 0.0000) (0.0000, 0.0000, 1.7441, 0.0000) (0.0000, 0.0000, 0.0000, 1.0000)> |
Теперь последний столбец не изменился, так как объект вернулся в координаты (0, 0, 0). Однако мы можем видеть, что три значения изменились, которые отвечают за масштабирование по X, Y и Z.
Вращение сложнее, так как вы можете вращаться вокруг трех осей, и каждое вращение представлено преобразованиями на двух других осях. Это немного выходит за рамки этого урока, поэтому я оставлю несколько ссылок в конце, если вы захотите углубиться в математику.
-------------------------------------------------------------------------------- World Matrix <Matrix 4x4 (-0.9182, 0.3398, -0.2037, 0.0000) (-0.2168, -0.8612, -0.4597, 0.0000) (-0.3316, -0.3780, 0.8644, 0.0000) ( 0.0000, 0.0000, 0.0000, 1.0000)> |
Вы также можете задаться вопросом о последней строке. Матрицы преобразования для трехмерного пространства фактически четырехмерные. Последняя строка — дополнительное измерение. Это математический «трюк», позволяющий матрице выполнять преобразования. Опять же, я не буду слишком углубляться в это. Проверьте ссылки в конце для получения дополнительной информации. В любом случае, это неважно для наших целей, поскольку этот ряд никогда не изменится.
Ниже наглядное изображение различных преобразований:
Использование матриц
Матрицы преобразования можно объединить, чтобы создать единую матрицу, которая включает в себя все результаты всех преобразований. Это делается путем их умножения. Это означает, что мы можем взять глобальную матрицу объекта и умножить ее на матрицу преобразования, чтобы получить новую матрицу, которая включает изменения обеих матриц. Затем мы можем присвоить ее как матрицу мира объекта и, таким образом, преобразовать объект.
obj.matrix_world *= some_transformation_matrix |
Пришло время вникнуть в этот класс Matrix и посмотреть, как мы можем использовать его для генерации матриц.
Перемещение
Начнем с самых простых: движущихся вещей. Все, что нам нужно сделать, это вызвать метод перемещения класса Matrix с вектором (или кортежем) значений для каждой оси.
translation_matrix = Matrix.Translation((0, 0, 2)) obj.matrix_world *= translation_matrix |
Масштабирование
Масштабирование принимает три аргумента. Первый — фактор масштаба. Второй — размер матрицы, он может быть либо 2 (2×2), либо 4 (4×4). Но поскольку мы работаем с 3D-объектами, то всегда должно быть 4. Конечные аргументы — это вектор, определяющий ось для масштабирования. Это может быть либо нулевое значение без масштабирования, либо 1 для масштабирования.
scale_matrix = Matrix.Scale(2, 4, (0, 0, 1)) # Scale by 2 on Z obj.matrix_world *= scale_matrix |
Вращение
Вращение принимает почти те же аргументы, что и масштаб. Первый — угол поворота в радианах. Второй — размер матрицы (такой же, как и раньше). Третий — ось вращения. Вы можете передать строку, такую как «X», «Y» или «Z», или вектор.
rotation_mat = Matrix.Rotation(math.radians(20), 4, 'X') obj.matrix_world *= rotation_mat |
Объединяя все это
Мы можем соединять преобразования вместе, умножая их один за другим. Но будьте осторожны, умножение матрицы не является коммутативным. Порядок имеет значение. Начните с перемещения, затем поворот и масштаб. Если вы получаете странные значения, которые выглядят как ошибки округления, посмотрите на порядок, в котором вы умножаете.
translation = (0, 0, 2) scale_factor = 2 scale_axis = (0, 0, 1) rotation_angle = math.radians(20) rotation_axis = 'X' translation_matrix = Matrix.Translation(translation) scale_matrix = Matrix.Scale(scale_factor, 4, scale_axis) rotation_mat = Matrix.Rotation(rotation_angle, 4, rotation_axis) obj.matrix_world *= translation_matrix * rotation_mat * scale_matrix |
Матрицы также могут использоваться для изменения меша вместо объекта. Для этого мы можем использовать метод transform(). Все, что он просит, это матрицу.
obj.data.transform(Matrix.Translation(translation)) |
Не забывайте, что мы также можем комбинировать матрицы путем умножения, чтобы вы могли сделать несколько преобразований за один раз.
obj.data.transform(translation_matrix * scale_matrix) |
Матрицы не только быстрее, но этот метод также выполняется прямо на C, поэтому он работает значительно лучше, нежели вычисление положения каждой вершины на Python (что довольно медленно и ресурсозатратно). Кроме того, мы можем сделать это в одну строку.
Потрясающе!
Финальный код
import bpy import math from mathutils import Matrix # ----------------------------------------------------------------------------- # Settings name = 'Cubert' # Origin point transformation settings mesh_offset = (0, 0, 0) origin_offset = (0, 0, 0) # Matrices settings translation = (0, 0, 0) scale_factor = 1 scale_axis = (1, 1, 1) rotation_angle = math.radians(0) rotation_axis = 'X' # ----------------------------------------------------------------------------- # Utility Functions def vert(x,y,z): """ Make a vertex """ return (x + origin_offset[0], y + origin_offset[1], z + origin_offset[2]) # ----------------------------------------------------------------------------- # Cube Code verts = [vert(1.0, 1.0, -1.0), vert(1.0, -1.0, -1.0), vert(-1.0, -1.0, -1.0), vert(-1.0, 1.0, -1.0), vert(1.0, 1.0, 1.0), vert(1.0, -1.0, 1.0), vert(-1.0, -1.0, 1.0), vert(-1.0, 1.0, 1.0)] faces = [(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)] # ----------------------------------------------------------------------------- # Add Object to Scene mesh = bpy.data.meshes.new(name) mesh.from_pydata(verts, [], faces) obj = bpy.data.objects.new(name, mesh) bpy.context.scene.objects.link(obj) bpy.context.scene.objects.active = obj obj.select = True # ----------------------------------------------------------------------------- # Offset mesh to move origin point obj.location = [(i * -1) + mesh_offset[j] for j, i in enumerate(origin_offset)] # ----------------------------------------------------------------------------- # Matrix Magic translation_matrix = Matrix.Translation(translation) scale_matrix = Matrix.Scale(scale_factor, 4, scale_axis) rotation_mat = Matrix.Rotation(rotation_angle, 4, rotation_axis) obj.matrix_world *= translation_matrix * rotation_mat * scale_matrix # ----------------------------------------------------------------------------- # Matrix Magic (in the mesh) # Uncomment this to change the mesh # obj.data.transform(translation_matrix * scale_matrix) |
Завершение
На этом данная часть серии подошла к концу. Если матрицы вас заинтересовали, вот несколько ссылок, чтобы узнать о них больше. Ссылки находятся в порядке сложности.
- World Matrix Documentation on Blender’s API
- Computerphile’s video on Matrices
- Matrices (Wikipedia)
- Transformation Matrices (Wikipedia)
- Matrix Multiplication (Wikipedia)
- Scratchapixel’s lesson on matrices
- Coding Labs’ article on Projection Transformation Matrices
Несколько вещей, которые вы можете попробовать сделать сами:
- Поместите это все в цикл и сделайте волну из несколько кубов или поверните каждый вокруг своей оси.
- Используйте матрицы, чтобы переместить центр куба, используя метод из этого урока.
- Попробуйте масштабировать меш без матриц или изменить координаты объекта (подсказка: не забудьте функцию vert())
- Попробуйте применить матрицу мира одного объекта другому.
На этом данный урок закончен, а в следующем мы продолжим изучение создания мешей с помощью Python и работе с ними.
Офигительно круто, действительно нужно знать программную организацию матриц в blender, что-то похожее я делал на matlabe
Хороший урок.
Скриптинг в Blender даже никогда не прикасался, хоть и немного пришлось оч.давно поработать на Python, правда на версии 1.7 уже 3-я.
Поэтому вопрос в эту тему, как актуальны книги в сегодняшнем дне :
Написание скриптов для Blender 2.49 — Anders Michel
Введение в написание скриптов на Питоне для Блендера 2.57 Thomas Larsson
На сколько эти книги устарели, есть ли более новые книги на русском? Можно ли эти книги за основу брать при освоении скриптинга в Blender?
Не взорвет ли моск тем кто вообще не в теме, уроки без элементарных основ? Могут просто пропустить без внимания.
Книги не актуальны, но если знать API современного Blender, то из них много чего можно почерпнуть. Иначе будет только хуже, поэтому лучше не усложнять себе жизнь.
«Войдите в матрицу[b][u].[/u][/b]»
«… это [s]не важно[/s][u][b]неважно[/b][/u] для наших целей …»
«Потрясающ[s]и[/s]е!»
«Labs’[b] [/b]article …» (пробіл між словами)
Пофиксил все, кроме матрицы. Там точка не обязательна, как и сама фраза. Это юмор автора.
А! Зрозумів ?
Спасибо, как программист Python приятно удивлён этими уроками.
P.S. Красивая картинка на превью. Сами делали?
Нет, превью из оригинала урока.
блин сидел-сидел, смотрел на вводную картинку, в уроке к сожалению не было как сделать тот объект выше, я подозреваю что не правильно сделал освещение и прочее, но пофиг, думал что сделать и получил вот этот странный объект, а он мне еще и понравился, можно анимировать и в свою игру вставить
«Войдите в матрицу[b][u].[/u][/b]»
«… это [s]не важно[/s][u][b]неважно[/b][/u] для наших целей …»
«Потрясающ[s]и[/s]е!»
«Labs'[b] [/b]article …» (пробіл між словами)
Пофиксил все, кроме матрицы. Там точка не обязательна, как и сама фраза. Это юмор автора.
А! Зрозумів ?
Хороший урок.
Скриптинг в Blender даже никогда не прикасался, хоть и немного пришлось оч.давно поработать на Python, правда на версии 1.7 уже 3-я.
Поэтому вопрос в эту тему, как актуальны книги в сегодняшнем дне :
Написание скриптов для Blender 2.49 — Anders Michel
Введение в написание скриптов на Питоне для Блендера 2.57 Thomas Larsson
На сколько эти книги устарели, есть ли более новые книги на русском? Можно ли эти книги за основу брать при освоении скриптинга в Blender?
Не взорвет ли моск тем кто вообще не в теме, уроки без элементарных основ? Могут просто пропустить без внимания.
Книги не актуальны, но если знать API современного Blender, то из них много чего можно почерпнуть. Иначе будет только хуже, поэтому лучше не усложнять себе жизнь.