Автор: Olivier Saraja
Перевод на английский язык: Ed Montgomery
Перевод на русский язык: Роман Володин, 2008
Оглавление:
• Настройка Питона
-Установка Pythonpath
-Задание пути по умолчанию для скриптов
• Предисловие и основные рекомендации
• Создание куба
- Создание грани или поверхности
- Использование циклов
- Создание куба
- Создание материала
• Добавляем камеру
• Добавление лампы
•Обзор кода
• Заключение
Мы благодарны Иву Байи (Yves Bailly) из GNU/Linux Magazine №68, за
возможность соединить Blender и Python для разработки графики. Мы
собираемся рассмотреть язык описания сцен POV-ray (подробно
рассматривался в предыдущих выпусках), чтобы понять как встроенный в
Блендер интерпретатор Питона даёт нам не менее широкие возможности, чем
полная версия интерпретатора Питона.
Но, прежде всего, как и во всех программах, необходимо определить
цель. В рамках изучения Питона, мы просто... попробуем воспроизвести
сцену Блендера, которая открывается по умолчанию! Мы будем учиться
создавать куб, присваивать ему материал, а затем добавим источник света
и камеру. Каждый из этих объектов будет расположен и направлен точно
также (в частности, камера и лампа), как в сцене по умолчанию.
Рисунок 1: Выглядит легко? Да, и мы узнаем как...
1.Настройка Питона
Большинство функций, которые я буду описывать, являются встроенными
в Python API для Blender. Но в некоторых случаях необходимо иметь
полную версию Питона на жестком диске. Есть некоторые модули
предоставляемые Питоном, которые нам понадобятся, например модуль math.
1.1 Установка Pythonpath:
Большинство дистрибутивов (очевидно, речь идет о Линуксе - прим.
перев.) включают в себя последние версии Питона, возможно он уже
установлен, осталось его только настроить. Необходимо указать системе
путь к Питону. Для того чтобы определить путь (или пути), есть простой
способ. Откройте консоль или терминал и введите команду python, а затем
подтвердите, нажав Enter.
Python 2.3.4 (#1, Feb 7 2005, 15:50:45)
[GCC 3.3.4 (pre 3.3.5 20040809)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Вы только что запустили Питон в консольном режиме. Теперь введите import и print sys.path. Вы увидите что-то вроде этого:
['', '/usr/lib/python23.zip', '/usr/lib/python2.3', '/home/olivier',
'/usr/lib/python2.3/plat-linux2', '/usr/lib/python2.3/lib-tk',
'/usr/lib/python2.3/lib-dynload', '/usr/lib/python2.3/site-packages']
Теперь откройте файл .bashrc из корня вашей домашней директории (/home/my). Добавьте в файл следующие строки:
export PYTHONPATH=/usr/lib/python23.zip:/usr/lib/python2.3: /usr/lib/python2.3/plat-
linux2:/usr/lib/python2.3/lib-tk:/usr/lib/python2.3/lib-
dynload:/usr/lib/python2.3/site-packages
Примечание о разделителях: (ни апострофов, ни путей, разделенных двоеточиями) это обусловлено командой print sys.path.
1.2 Задание пути по умолчанию для скриптов:
Запустите Blender, и нажмите на иконку, расположенную в нижней части 3D-окна, и выберите User Preferences.
Рисунок 2: Выберите User Preferences
Здесь вы можете настроить окна видов, управление, язык, темы,
автосохранение и многое другое. Нас интересует категория File Paths.
Щелкните на ней и введите путь к папке, куда вы будете сохранять ваши
скрипты.
Рисунок 3: Готово! Путь к скриптам задан
Осталось вернуться в 3D-окно и нажать CtrL+U, чтобы сохранить изменения.
2. Предисловие и основные рекомендации
Я настоятельно рекомендую вам запускать Блендер из консоли или
терминала, в этом случае вы сможете увидеть сообщения интерпретатора
при выполнении ваших скриптов. В этом случае, если ваш код содержит
ошибки, вы сможете их легче найти и исправить их. После запуска
Блендера разделите 3D-окно на два, щелкнув правой кнопкой на заголовке
внизу окна и выбрав Split Area.
Рисунок 4: Подготовка рабочего пространства
В правой части окна откройте окно текстового редактора (Shift+F11)
и создать новый файл с помощью комбинации Alt+N или используйте меню
под окном: File > New. Включите нумерацию строк, это будет очень
удобно.
Первые несколько строк довольно просты. Прежде всего, создадим «скелет» нашего скрипта:
01: import Blender
02: from Blender import Nmesh, Scene, Object
03: Blender.Redraw()
На данный момент, программа не делает абсолютно ничего. Тем не
менее, вы можете выполнить скрипт, нажав комбинацию [Alt]+ [P]
(позаботьтесь, чтобы курсор мыши при этом находился в текстовом окне).
3. Создание куба
Было бы здорово, если питон позволил нам выполнить некую команду, например, cube { },
как в случае с POV-Ray. Но мы будем работать не с примитивами, а с
компонентами сетки. Мы собираемся проиллюстрировать и снабдить массой
комментариев этот простой процесс.
3.1 Создание грани или поверхности
Начнем с простого. Давайте предположим, что мы хотим плоскость как
на рисунке 5. Он состоит из 4 точек или вершин, которые объединены
вместе, образуя грань. Тогда легко задать координаты четырех вершин.
Для удобства назовем их числами от 1 до 4, или вернее от 0 до 3. В
действительности, в компьютерных науках, отсчет ведется с 0, а не с 1.
Нужно немного практики, чтобы к этому привыкнуть.
vertex 0: [-1,-1,0]
vertex 1: [-1,+1,0]
vertex 2: [+1,+1,0]
vertex 3: [+1,-1,0]
Теперь мы легко можем определить нашу поверхности, которая опирается на точки от 0 до 3:
face 1: [0, 1, 2, 3]
Рисунок 5: Грань состоит из четырех вершин, обозначенные 0 - 3
Теперь мы можем написать следующий код. Первые две строки
инициализируют модули Блендера для Питона. Сейчас мы собираемся
сосредоточиться на создании сетки, для этого нам нужны функции модуля
Nmesh.
01: import Blender
02: from Blender import Nmesh
В следующей строке мы создаем в памяти объект типа mesh (сетка), но
на данный момент он является абсолютно пустым. Мы только
зарезервировано место в памяти для него, с именем plandata. Избегайте
использования пропусков в именах, иначе это может вызвать ошибку.
03: plandata = NMesh.GetRaw ()
Теперь мы определим координаты четырех точек, которые составляют
нашу поверхность. Имя vertex, выбранное произвольно, и координаты
прилагаемого вектора (Nmesh.Vert) показаны на рисунке 5. В следующей
строке мы добавляем в список компонент сетки (plandata) координаты
вершин (vertex) в список (Nmesh.Vert). Мы будем повторять эти две
строки кода для каждой вершины нашей грани, лишь изменяя координаты. Во
внутренней структуре координаты первой вершины расположены в переменной
verts [0], второй вершины в verts [1] и т.д. для N-й вершины в
диапазоне verts [N-1].
04: vertex=NMesh.Vert(-1, -1, 0)
05: plandata.verts.append(vertex)
06: vertex=NMesh.Vert(-1, +1, 0)
07: plandata.verts.append(vertex)
08: vertex=NMesh.Vert(+1, +1, 0)
09: plandata.verts.append(vertex)
10: vertex=NMesh.Vert(+1, -1, 0)
11: plandata.verts.append(vertex)
Теперь мы зарезервируем память для грани под простым именем face:
12: face = NMesh.Face ()
Теперь мы можем определить вершин, которые составляют грань. Это
делается просто путем добавления verts [0] - verts [3] в список вершин
(v), которые образуют грань face плоскости plandata. В строках ниже
только имена face и plandata задаются пользователем, остальные
относятся к внутренним функциям и переменным модуля Питон в Блендере.
13: face.v.append(plandata.verts[0])
14: face.v.append(plandata.verts[1])
15: face.v.append(plandata.verts[2])
16: face.v.append(plandata.verts[3])
Вершины, которые составляют нашу грань, определены. Теперь нам надо добавить грань face в список граней объекта plandata.
17: plandata.faces.append (face)
Всё, сетка полностью определена и построена. Тем не менее, она
существует только в память вашего компьютера. Мы собираемся превратить
её в геометрию Блендера.
18: NMesh.PutRaw(plandata,'Plan',1)
Blender теперь готов отобразить сетку, просто необходимо обновить экран и пользователь будет возвещен о своем успехе.
19: Blender.Redraw ()
3.2 Использование циклов
Представьте себе, что я должен описать сетку, состоящую из
нескольких сотен граней. Я бы сошел с ума от бесконечных
[name.vertex]=NMesh.Vert([coordinates])N[name.data].verts.append[name.vertex]Nx[name.face].v.append([name.data].verts[number.vertex]).
Я бы гораздо скорее изобрел инструмент, который бы сделал это за меня.
К счастью, у нас есть циклы.
Посмотрим, что можно поместить в цикл, который будет повторяться
столько раз, сколько нужно точек. Для этого мы собираемся создать
вершины по их координатам.
# Определение вершин
list of vertices=[
[-1,-1,0], # vertex 0
[-1,+1,0], # vertex 1
[+1,+1,0], # vertex 2
[+1,-1,0] # vertex 3
]
Тогда, в момент создания вершин, мы потребуем от Питона не делать
их по одной, пройти через вектор list_of_vertices, которые мы
определили, и каждую из них поместить в список с именами composante[i].
Дальше мы создадим вершины для объекта plandata.
for composante in list_of_vertices:
vertex=NMesh.Vert(composante[0], composante[1], composante[2])
plandata.verts.append(vertex)
Внимание
Важно запомнить:
- Не забывайте двоеточие в конце строки for ... in ... : По сути, это указывает на начало цикла.
- Выдерживайте отступы в циклах и функциях. По ним Питон отличает начало и конец цикла.
- Вы можете поместить цикл в цикл, но помните об отступах! Иначе Питон выдаст ошибку.
3.3 Создание куба
Теперь мы знаем, как определить перечень вершин, а также как
использовать циклы для упрощения создания вершин. Теперь все мы должны
научиться создавать списки граней и использовать дополнительные циклы
для автоматической генерации граней Мы задали координаты вершин куба:
face 0: [0,1,2,3]
face 1: [4,5,6,7]
face 2: [0,4,7,3]
face 3: [1,2,6,5]
face 4: [0,1,5,4]
face 5: [3,7,6,2]
Затем по вершинам построили грани (номера вершин в скобках):
face 0: [0,1,2,3]
face 1: [4,5,6,7]
face 2: [0,4,7,3]
face 3: [1,2,6,5]
face 4: [0,1,5,4]
face 5: [3,7,6,2]
Рисунок 6: Куб состоит из восьми вершин (0-7)
Мы можем обобщить эту информацию: list_of_vertices будет содержит координаты всех точек, а list_of_faces содержит грани:
# Определение вершин
list_of_vertices=[
[-1,-1,-1], # vertex 0
[-1,+1,-1], # vertex 1
[+1,+1,-1], # vertex 2
[+1,-1,-1], # vertex 3
[-1,-1,+1], # vertex 4
[-1,+1,+1], # vertex 5
[+1,+1,+1], # vertex 6
[+1,-1,+1] # vertex 7
]
# Определение граней
list_of_faces=[
[0,1,2,3], # face 0
[4,5,6,7], # face 1
[0,4,7,3], # face 2
[1,2,6,5], # face 3
[0,1,5,4], # face 4
[3,7,6,2] # face 5
]
Как и раньше, мы в памяти создадим сетку, пока пустую. Назовем её CubeMeshData.
CubeMeshData = NMesh.GetRaw ()
А сейчас мы запустим в работу наши волшебные циклы. Для каждой
вершины list_of_vertices мы создадим новую вершину, используя
координаты для указания её положения. Позже мы можем добавить эту
вершину в список вершин сетки.
for composante in list_of_vertices:
vertex=NMesh.Vert(composante[0], composante[1], composante[2])
CubeMeshData.verts.append(vertex)
Мы также собираемся создать цикл для создания граней. Сначала
создадим грани из перечисленных в list_of_faces; грань остается в
памяти, пока мы не добаывим её вершины. Это будет выполнять второй
цикл, заключенный в первый.
for face_current in list_of_faces:
face=NMesh.Face()
for number_vertex in face_current:
face.append(CubeMeshData.verts[number_vertex])
CubeMeshData.faces.append(face)
Очень просто, да? Но на самом деле, наш объект обладает смехотворно
малым числом вершин и граней. Теперь, когда наша сетка окончательно
определена, нам нужно передать информацию Блендеру, так чтобы он смог
её использовать. Это следующая строка: Блендер из ячеки памяти
CubeMeshData создаст объект Cube. Кроме того, последний аргумент
указывает, что Блендер должен пересчитать нормали сетки. Эта операция
не является обязательной, но мы рекомендуем выполнять её.
NMesh.PutRaw(CubeMeshData,'Cube',1)
3.4 Создание материала
Для определения материала, мы собираемся взглянуть на раздел
Shading (F5) чтобы увидеть значения, которые мы собираемся
воспроизвести. А затем назначим материал кубу.
Рисунок 7: Свойства материала
Базовый синтаксис
[name.data] = Material.New('MA:Blender')
[name.object.receiving.the.material].materials.append[name.data]
[name.object].link([name.data])
[name.scene.current].link([name.object])
Создание материала является относительно простой задачей.
Достаточно создать в памяти объект с именем материала, а потом
использовать следующую строку, что иметь доступ к материалу в Блендере
(вкладка Материалы).
# Определение материала
mat = Material.New('Material')
Если материал создан он не используется в данный момент какой-либо
сеткой или объектом. Мы собираемся добавить материал "mat" в список
материалов CubeMeshData (заметим, что это ограничено 16-ю символами).
CubeMeshData.materials.append(mat)
Теперь мы собираемся определить некоторые свойства этого материала,
начиная с компонентов цвета (R, G и B равны 0.756, чтобы получить
светло-серый), альфы материала (1.0, чтобы получить совершенно
непрозрачный куб), отражение света (Ref 0,8), ее Specularity (0.5 для
средней интенсивности зеркального отражения).
mat.rgbCol = [0,756, 0,756, 0,756]
mat.setAlpha (1,0)
mat.setRef (0,8)
mat.setSpec (0,5)
mat.setHardness (50)
Конечно, число параметров гораздо больше, но нам достаточно этого.
4. Добавляем камеру
Создание камеры еще проще, чем создание куба. Но из-за некоторых особенностей чуть сложнее
создания материала.
Базовый синтаксис:
[name.data] = Camera.New('[type]','[CA:Blender]')
Определение текущей сцены:
[name.scene.current] = Scene.getCurrent()
[name.object] = Object.New('[OB:Blender]')
[name.object].link([name.data])
[name.scene.current].link([name.object])
[name.scene.current].setCurrentCamera([name.object])
Некоторые опции:
[type] - тип, может быть "persp" или "ortho"
[name.data].lens = [значение] - линза камеры (по умолчанию, 35)
[name.data].clipStart = [значение] - устанавливает начало поля зрения камеры (по умолчанию
0.10)
[name.data]. clipEnd = [значение] - устанавливает конец поля зрения камеры (по умолчанию
100.00)
Мы собираемся приступить к созданию объекта в памяти и дадим ему имя "С". Камера будет перспективная:
c = Camera.New('persp','Camera')
Линза равна 35 (хотя можно было оставить значение по умолчанию)
c.lens = 35.0
Затем определите имя файла данных для текущей сцены "cur":
cur = Scene.getCurrent ()
Мы уже фактически создали объект-камеру в Blender. Он будет нести простое название "ob" для объекта, и "Camera" в Blender.
ob = Object.New('Camera')
Отныне объект в блендере существует, но надо присоединить его к камере:
ob.link (с)
А камеру нужно присоединить к сцене:
cur.link (ob)
Камера сначала объявляется как данные, затем как объект, и наконец как камера. Теперь сделаем камеру текущей:
cur.setCurrentCamera (ob)
И вуаля, у нас есть камера! Теперь мы должны определить позицию,
вращение и масштаб камеры. В данном случае, нас интересует только
позиция и вращение. Давайте еще раз посмотрим на сцену. В 3D-окне
выбирем камеру и нажмем N. Появится окно Transform Properties, где
можно увидеть название объекта "ob" и значения трансформаций.
Рисунок 8: Свойства трансформаций камеры
Базовый синтаксис трансформаций:
[name.objeсt]. setEuler (angX, angY, angZ)
[name.object]. setLocation (posX, posY, posZ)
[name.object]. setSize (sizX, sizY, sizZ)
setEuler позволяет определить углы вращения объекта в его локальных
осях. setLocation определяет положение в мировых осях (относительно
сцены). Наконец, setSize позволяет определить пропорции объекта в трех
направлениях локальных осей. Имейте в виду, что углы angX, angY и angZ
должны быть выражены в радианах.
ob.setLocation (6,283, -5,000, 5,867)
Так же определяются значения поворотов данного объекта. К
сожалению, угол в Питоне должны быть выражен в радианах, хотя в
Блендере они выражаются в градусах. Т.е. надо перевести радианы в
градусы. Как вы знаете 360 градусов равны 2пи радиан (пи = 3.14159...),
в результате 1 градус = (2пи/360):
RotX = 52,928 * 2пи/360
RotY = -1,239 * 2пи/360
RotZ = 52,752 * 2пи/360
В Питоне пи пишется как math.pi, и требует подключения дополнительного модуля - math. Т.к. мы ленивы, определим следующее:
Conv = 2 * math.pi/360
что позволит нам писать следующее преобразование так:
ob.setEuler(52.928*conv, -1.239*conv, 52.752*conv)
Окончательно код создания камеры выглядит так:
c = Camera.New('persp','Camera')
c.lens = 35.0
cur = Scene.getCurrent()
ob = Object.New('Camera')
ob.link(c)
cur.link(ob)
cur.setCurrentCamera(ob)
ob.setEuler(52.928*conv, -1.239*conv, 52.752*conv)
ob.setLocation(6.283, -5.000, 5.867)
также, в первой строке скрипта должно быть:
import Blender, math
5. Добавление лампы
Если вы поняли, как добавить камеру скрипт, добавление лампы не составит труда.
Единственная сложность может быть в разнообразии ламп, т.к. у каждого типа свои параметры.
Мы не будем подробно их рассматривать, просто продемонстрируем методы.
Базовый синтаксис лампы:
[name.data] = Lamp.New('[type]','LA:Blender')
[name.object] = Object.New('OB:Blender')
[name.object].link([name.data])
[name.scene.current].link([name.object])
Некоторые параметры:
['type'] тип - может быть 'Lamp', 'Sun', 'Spot', 'Hemi', 'Area' или 'Photon'
Если вы выберете тип "Spot", вы сможете установить дополнительные параметры для него.
Например:
l.setMode('square', 'shadow')
создаст источник квадратной формы и включит тени.
Снова зарезервируем место в памяти, назвав его "l":
l = Lamp.New('Lamp','Lamp')
Фактическое создание объекта лампы тривиально. В Питоне это просто название "ob" для объекта, и "Lamp" в Блендере.
ob = Object.New('Lamp')
Объект "ob" теперь существует в Blender, но объект надо связать с нашей лампой:
ob.link (l)
В свою очередь объект "ob" должны быть связаны с текущей сценой (текущую сцену мы определили ранее - cur):
cur.link (ob)
Наша лампа готова, надо разместить её в сцене. Посмотрим на свойства трансформации лампы.
Рисунок 9: Свойства лампы
Следует отметить удивительную деталь: по умолчанию лампа просто
нематериальная точка в пространстве, которая служит в качестве
источника света. Не обязательно прописывать её свойства. Тем не менее,
сделать это необходимо.
Как и ранее, название объекта лампы просто "ob". Как и раньше:
ob.setLocation (0.000, -10.000, 7.000)
Определение вращения лампы также не сложно:
ob.setEuler(47.534*conv, 0.000, 0.000)
Окончательный блок кода по созданию лампы:
l = Lamp.New('Lamp','Lamp')
ob = Object.New('Lamp')
ob.link(l)
cur.link(ob)
ob.setLocation(0, -10, 7)
ob.setEuler(47.534*conv,0,0)
6. Обзор кода
Мы рассмотрим здесь полный код, который мы видели до настоящего
времени по частям, хоть и с нумерацией строк. Вы найдете его на диске к
журналу или сайте http://www.linuxgraphic.org под названием
blender-default-scene.py и blender-default-scene.blend
01: import Blender, math
02: from Blender import Camera, Object, Scene, Lamp, NMesh, Material
03:
04: conv = 2*math.pi/360
05:
06: # Definition of vertices
07: list_of_vertices=[
08: [-1,-1,-1], # vertex 0
09: [-1,+1,-1], # vertex 1
10: [+1,+1,-1], # vertex 2
11: [+1,-1,-1], # vertex 3
12: [-1,-1,+1], # vertex 4
13: [-1,+1,+1], # vertex 5
14: [+1,+1,+1], # vertex 6
15: [+1,-1,+1] # vertex 7
16: ]
17:
18: # Definition of faces
19: list_of_faces=[
20: [0,1,2,3], # face 0
21: [4,5,6,7], # face 1
22: [0,4,7,3], # face 2
23: [1,2,6,5], # face 3
24: [0,1,5,4], # face 4
25: [3,7,6,2] # face 5
26: ]
27:
28:
29: # Definition of a cube
30: CubeMeshData=NMesh.GetRaw()
31:
32: ## Definition of a material
33: mat = Material.New('Material')
34: CubeMeshData.materials.append(mat)
35: print mat.rgbCol
36: mat.rgbCol = [0.756, 0.756, 0.756]
37: mat.setAlpha(1.0)
38: mat.setRef(0.8)
39: mat.setSpec(0.5)
40: mat.setHardness(50)
41:
42: for component in list_of_vertices:
43: vertex=NMesh.Vert(component[0], component[1], component[2])
44: CubeMeshData.verts.append(vertex)
45:
46: for face_current in list_of_faces:
47: face=NMesh.Face()
48: for number_vertex in face_current:
49: face.append(CubeMeshData.verts[number_vertex])
50: CubeMeshData.faces.append(face)
51:
52: NMesh.PutRaw(CubeMeshData,'Cube',1)
53:
54: # Definition of the camera
55: c = Camera.New('persp','Camera')
56: c.lens = 35.0
57: cur = Scene.getCurrent()
58: ob = Object.New('Camera')
59: ob.link(c)
60: cur.link(ob)
61: cur.setCurrentCamera(ob)
62: ob.setEuler(52.928*conv, -1.239*conv, 52.752*conv)
63: ob.setLocation(6.283, -5.000, 5.867)64:
65: # Definition of the lamp
66:
67: l = Lamp.New('Lamp','Lamp')
68: ob = Object.New('Lamp')
69: ob.link(l)
70: cur.link(ob)
71: ob.setLocation(0, -10, 7)
72: ob.setEuler(47.534*conv,0,0)
73:
74: Blender.Redraw()
7. Заключение
Мы добрались до конца первой статьи. Мы изучили базовый синтаксис,
необходимый для получения некоторых типов объектов в Blender.
Программирование на Python в Blender является немного более сложным,
чем написание кода под POV-Ray, потому что необходимо вызвать
внутренние функции Blender, получить результаты, а затем вернуть их в
Blender. В конце я приведу несколько полезных ссылок.
Ссылки
Официальный сайт Блендера: http://www.blender.org
Документация к модулям Blender API (для версии 2.48): http://www.blender.org/documentation/248PythonDoc/index.html
Документация к Питону: http://www.python.org/
Уроки от JM Soler: http://jmsoler.free.fr/didacticiel/blender/tutor/index.htm
Источник: http://blender3d.org.ua/tutorial/ |