четверг, 13 июля 2017 г.

Процедурная генерация .


Процедурная генерация великолепна! В этом уроке мы рассмотрим создание меша с использованием блендеровского Python API.Создание мешей программными способами открывает множество возможностей. Вы можете создавать параметрические объекты, которые отвечают размерам реального мира, генеративное искусство, формы на основе математических формул или даже процедурный контент для игр. Blender – отличный выбор для такого рода работ, поскольку он сочетает в себе полномасштабный набор инструментов моделирования и анимации с мощным (и достаточно хорошо документированным) Python API.
В этой серии мы рассмотрим создание нескольких примитивов и некоторых базовых преобразований, а также рассмотрим некоторые ухищрения, которые упростят процесс разработки дополнений. Я предполагаю, что вы уже знаете Python на базовом уровне и достаточно знакомы с Blender. Поэтому мы пропустим введение в мир 3D и сосредоточимся на создании простой плоской сетки.Совет для профессионалов: Сохраняйтесь как можно чаще! Blender может вылететь неоднократно :)Настройка.В системе данных Blender существует различие между данными меша и объектами в сцене. Мы должны добавить меш и связать его с объектом, а затем связать этот объект со сценой, прежде чем мы сможем увидеть какие-либо результаты.Начнем с импорта bpy (сюрприз!) и создания некоторых переменных.
import bpy

# Settings
name = 'Gridtastic'
rows = 5
columns = 10
В переменной Name будет храниться имя меша и объекта одновременно. В то время как переменные rows и columns будут контролировать количество вершин нашей сетки.Далее займемся созданием меша и объекта. Первым делом мы должны добавить блоки данных меша, затем объект, который будет их использовать и в завершении связать их с текущей сценой. Я также добавил несколько пустых списков для вершин и граней. Мы будем их заполнять немного позже.
verts = []
faces = []

# Create Mesh Datablock
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(verts, [], faces)

# Create Object and link to scene
obj = bpy.data.objects.new(name, mesh)
bpy.context.scene.objects.link(obj)

# Select the object
bpy.context.scene.objects.active = obj
obj.select = True
Наиболее интересная часть это from_pydata(). Эта функция создает меш из трех списков: вершины, ребра и грани. Обратите внимание, что если мы указываем грани, то ребра указывать уже не нужно. Проверьте документацию по данной функции.Попробуйте запустить скрипт сейчас. Вы увидите, что добавлен новый объект, но геометрии не видно (поскольку мы еще не добавили ее). Удалите этот объект и давайте двинемся дальше, чтобы узнать, как же создать 2D сетку.Сетка вершин.Начнем с добавления одной вершины. Вершины представляются тремя координатами (X, Y и Z). Наша сетки будет двухмерной, поэтому мы будем беспокоиться лишь об X и Y, Z у нас всегда будет равно 0. Мы поместим вершину в центр сцены, которая также является центром глобальных координат. Другими словами, ее координаты равны (0, 0, 0). Каждая вершина является кортежем из 3 чисел с плавающей запятой, поэтому измените список следующим образом:
verts = [(0, 0, 0)]
Запустите скрипт снова и вы увидите одинокую точку. Теперь вы можете перейти в режим редактирования и поиграть с ней. Давайте двинемся дальше и создадим ряд вершин. Для этого нам понадобится цикл, который добавит столько вершин, сколько столбцов мы установили в переменную columns. Это очень легко сделать с помощью выражения:
verts = [(x, 0, 0) for x in range(columns)]
range () возвращает целые числа, поэтому координата вершины по оси X будет их номером столбца. Это означает, что ширина каждого столбца будет 1 Blender единица (или 1 метр). Запустите скрипт снова. Вы увидите 10 вершин, выстроенных по оси X. Чтобы завершить создание сетки, нам нужно больше одной строки. Мы можем легко расширить список для добавления строк:
verts = [(x, y, 0) for x in range(columns) for y in range(rows)]
Теперь мы видим сетку вершин во всей ее красе.Мы можем приступать к созданию граней, но сначала нам нужно понять, как это делать.Разбираемся с гранями.Каждая вершина, которую мы добавили имеет свой индекс. Индексы устанавливаются в том порядке, в котором мы создаем наши вершины, поэтому у первой вершины индекс 0, у второй 1 и т.д.Чтобы создать грань, нам нужно добавить кортеж индексов вершин в список граней. Этот кортеж может состоять из 3 (треугольник), 4 (четырехугольник) или большего (многоугольник) количества индексов. Кстати, это все целые числа. Поскольку мы будем создавать четырехугольные грани, нам необходимо будет указать 4 индекса для каждой из них. Но как? Вы можете попытаться угадать их, но есть лучший способ. Мы можем включить режим отладки в Blender. Откройте консоль Python в Blender и введите следующее:
bpy.app.debug = True
Это самая полезная настройка, которую вы можете использовать для создания мешей и даже для разработки аддонов. Чтобы увидеть индексы вершин, выберите сетку двумерных вершин и перейдите в режим редактирования. Откройте панель свойств и активируйте опцию Indices в меню Mesh Display. Если вы не видите опцию, возможно, вы еще не включили режим отладки. Теперь любая выбранная вершина покажет ее индекс, поэтому выберите их все, чтобы увидеть их индексы.Давайте сосредоточимся на первой грани. Как видите, она состоит из вершин 0, 1, 5 и 6. Попробуем сделать одну грань с этими индексами:
faces = [(0, 1, 5, 6)]

Попробуйте запустить скрипт еще раз и… подождите, что-то не так! Похоже, мы связали неправильные вершины.
Ну, мы соединили правильные вершины, но в неправильном порядке. Да, при создании граней необходимо соблюдать порядок: против часовой стрелки, начиная с нижней левой вершины.Поэтому порядок для грани будет 0, 5, 6, 1. Исправьте это и снова запустите скрипт.Теперь мы в деле. Каждый раз, когда вы видите подобные проблемы, попробуйте поменять первый или два последних набора индексов между собой. Хорошо, это то место, где все становится интересным. Нам нужно выяснить, как вычислить все индексы, чтобы создать целый ряд граней. Если мы внимательно посмотрим на индексы вершин, мы увидим закономерность:
  • Все индексы увеличиваются на 5 по оси X. Их количество равно количеству столбцов.
  • Первый индекс начинается с 0, второй на 1 больше.
Мы можем вычислить первый индекс в цикле, умножив текущий столбец на строку. Поскольку второй смещен на 1, нам просто нужно добавить 1, чтобы получить его.Попробуйте вывести данные значения в консоли, предварительно определив переменные columns и rows:
for x in range(columns - 1):
     print(x * rows)
     print(x * rows + 1)
Возможно, вам интересно, почему мы перебираем столбцы – 1. У нас есть 10 столбцов вершин, но они создают только 9 столбцов граней. Последний столбец ни с чем не соединяется.Третий и четвертый индексы (x + 1) * rows + 1 и (x + 1) * rows соответственно. Мы добавляем 1 к X до умножения, чтобы установить индекс в следующую строку.Вот цикл для вывода всех индексов:
for x in range(columns - 1):
     print('first:', x * rows)
     print('second:', x * rows + 1)
     print('third:', (x + 1) * rows + 1)
     print('fourth:', (x + 1) * rows)
     print('---')
Создание сетки.Вооружившись всеми этими знаниями, мы можем построить первый ряд граней. Но прежде чем мы дойдем до этого, давайте выделим код граней в его собственную функцию, чтобы мы могли сохранить код в чистоте и порядке. Я также добавил возможность для создания граней в любой строке. Строки увеличивают индексы на 1, поэтому мы можем просто добавить номер строки в конце.
def face(column, row):
    """ Create a single face """

    return (column * rows + row,
            column * rows + 1 + row,
            (column + 1) * rows + 1 + row,
            (column + 1) * rows + row)
Создадим выражение, подобное тому, что мы делали с вершинами:
faces = [face(x, y) for x in range(columns - 1) for y in range(rows - 1)]
Мы используем строки – 1 по той же причине, что и столбцы. Запустите скрипт и созерцайтеВот теперь сетка завершена. Вы создали скрипт, который может создавать 2D-сетки! Похлопайте себя по плечу и продолжайте читать, чтобы немного улучшить и расширить наш скрипт.Масштабирование.Мы можем контролировать количество вершин нашей сетки, но квадраты всегда равны 1 BU (или 1 метр). Давайте изменим это.Все, что нам нужно сделать, это умножить координаты X и Y на коэффициент масштаба. Начните с добавления переменной размера (size). Мы можем добавить это непосредственно к выражению verts, но опять же, будет лучше и чище, если мы сделаем это в своей собственной функции.
size = 1

def vert(column, row):
    """ Create a single vert """

    return (column * size, row * size, 0)


verts = [vert(x, y) for x in range(columns) for y in range(rows)]

Попробуйте установить размер во что-то отличное от 1 и проверьте сетку.

Финальный код.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import bpy

# Settings
name = 'Gridtastic'
rows = 5
columns = 10
size = 1

# Utility functions
def vert(column, row):
    """ Create a single vert """

    return (column * size, row * size, 0)


def face(column, row):
    """ Create a single face """

    return (column* rows + row,
            column * rows + 1 + row,
            (column + 1) * rows + 1 + row,
            (column + 1) * rows + row)


# Looping to create the grid
verts = [vert(x, y) for x in range(columns) for y in range(rows)]
faces = [face(x, y) for x in range(columns - 1) for y in range(rows - 1)]

# Create Mesh Datablock
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(verts, [], faces)

# Create Object and link to scene
obj = bpy.data.objects.new(name, mesh)
bpy.context.scene.objects.link(obj)

# Select the object
bpy.context.scene.objects.active = obj
obj.select = True


Завершение.
Надеюсь, вам понравилось это введение в создание мешей на Python. Это только самый простой пример и есть много других интересных вещей. Вот несколько простых вещей, которые вы можете попытаться реализовать самостоятельно:
  • Используйте два масштабирующих фактора: для X и Y.
  • Добавьте смещение, чтобы сетка начиналась не с координат (0, 0).
  • Изолируйте все это в свою собственную функцию (или класс).







Комментариев нет:

Отправить комментарий