在本教程中,我们将学习一些 Blender 脚本技术,比如如何使用代码处理、操作、复制和动画网格图元。要结合所有这些技术,我们将创建一个波浪形的锥形图案:一个看起来很酷的动画,你可以将其转换为循环 GIF。
我将使用bpy.data模块中的一系列属性和方法来回顾最重要的bpy库。我还将介绍如何从其他 Python 文件导入代码,以及如何使用其他代码编辑器来编写 Blender 代码。当然,使用 Blender 进行创意编码还有很多其他内容,但这就是我在这个简短的教程系列中所涵盖的全部内容。
在继续之前,启动 Blender(使用命令行)。如果已经打开它,请使用File > New > General创建一个新的 Blender 文件。可以看到一个新场景,其中一个立方体位于 xyz 坐标 (0, 0, 0)。
一、导入 bpy
bpy 库是所有魔术发生的原因。它包含九个主要模块,使你能够使用 Python 控制 Blender;它们是bpy.app, bpy.context, bpy.data, bpy.msgbus, bpy.ops, bpy.path, bpy.props, bpy.types, 和bpy.utils. 在 Python 控制台中,bpy库会自动导入并立即可用。但是,当你使用文本编辑器(或任何其他代码编辑器)编写 Python 脚本时,必须先添加必要的import行,然后才能使用bpy。
注意:除了bpy,Blender 还包括几个独立的模块,例如aud用于音频,以及mathutils用于操作矩阵、欧拉、四元数和向量。
切换到 【Scripting】 选项卡,然后在【 Text Editor 】中单击 【New】 以创建新的 Python 脚本。
接着,将以下代码添加到新脚本中以导入bpy并打印场景中的对象列表:
import bpy
print(bpy.data.objects)
运行脚本(使用 Alt-P快捷键 或者 ▶ 按钮),终端应显示:
其中,bpy_collection[3]部分表示存在三个对象:相机、立方体和灯光。如果你在场景中添加或删除任何内容,[3] 会变化以反映对象的总数。
不过,bpy.data.objects 还有更多的作用. 例如,你可以使用它来处理 Outliner 列表中的特定项目。
二、选择对象
通过上面内容,我们使用 Python 控制台来影响在 3D 视口中选择的对象。不过,更多时候,我们会希望通过 Python 脚本来处理对象,而不依赖于 GUI 中选择的内容。可以使用bpy按名称、对象在对象序列中的位置或某些其他属性来选择对象。如果使用的是bpy.context,则必须在 3D 视口中选择立方体(因此它是橙色的)以使用 Python 代码对其进行操作。使用bpy.data.objects,可以处理对象,而不管 Blender 界面中的活动是什么。
使用 Pythonlist()函数 处理bpy.data.objects 以打印场景中的对象列表:
print(list(bpy.data.objects))
运行脚本时,它应该在终端中显示以下输出:
[bpy.data.objects['Camera'], bpy.data.objects['Cube'],
可以使用其键(项目名称)或索引(序列中的顺序)来寻址任何对象。索引值从零开始——所以 Camera 是 item 0, Cube 是 item 1,等等。可以使用bpy.data.objects['Cube']或bpy.data.objects[1]来寻址多维数据集。接下来,我们将使用不同的属性和方法来操作多维数据集。
三、属性和方法
属性就像属于对象的变量。对象的数据类型决定了它的属性。例如,立方体(由顶点组成的三维网格)包括其尺寸、坐标等属性。立方体的数据类型是 bpy_types.Object ,可以通过在终端或控制台中运行 type(bpy.data.objects['Cube']) 来确认这一点。
location属性(众多bpy_types.Object属性之一)包含立方体的坐标,可以使用它来重新定位对象:
bpy.data.objects['Cube'].location = (3, 0, 0)
这会将立方体定位在 xyz 坐标 (3, 0, 0)。在这种情况下,只会影响 x 坐标,因此可以使用:
bpy.data.objects['Cube'].location[0] = 3
请注意,.location[0]是 (3, 0, 0)中第一个 (x) 值的索引。或者,可以尝试该.location.x属性,这可以说是最直观的可读版本:
bpy.data.objects['Cube'].location.x = 3
运行脚本,立方体应移动到新位置,沿 x 轴距离场景中心三个单位:
由于我们已启用 python tooltips,因此如果将鼠标指针悬停在“对象属性”面板中的任何字段上,则会有一个工具提示指示如何在 Python 中为该特定对象处理该特定属性。该面板通常位于 Blender 界面的右下方区域,位于布局和脚本工作区中。在下图 中,工具提示显示了立方体 x 坐标的 Python 详细信息:
如果右键单击此字段,则会有一个名为Online Python Reference的菜单选项,它会在 Web 浏览器中打开该location属性的相关文档:
记下浏览器地址栏中的 URL:https://docs.blender.org/api/2.83/bpy.types.Object.html#bpy.t...。尤其是最后一个斜线之后的内容。加载/bpy.types.Object.html网页,然后是#bpy.types.Object.location跳转/滚动到location条目。还可以使用左栏中的搜索功能和链接导航参考。有时使用你的网络浏览器搜索功能(Ctrl+F或Cmd+F)在给定页面上快速查找内容会很方便。如果想尝试其他属性,不妨试试scale。
方法就像属于对象的函数。它们执行操作——例如,bpy.ops.mesh模块包括几种用于创建新网格的方法。可以很容易地区分属性和方法,因为方法的末尾有一对括号。在本节中,我们将使用不同的方法在场景中添加和删除对象。
primitive_cone_add() 方法将构造一个圆锥网格,换句话说,可以使用此方法将圆锥体添加到场景中。将此新行添加到脚本的末尾:
bpy.ops.mesh.primitive_cone_add()
当运行代码时,这应该会在场景中添加一个新的锥体。
primitive_cone_add()方法还可以接受参数来指定圆锥半径、深度、位置、旋转、比例等。在脚本中添加第二条锥形,其中包含一个控制其位置的参数:
bpy.ops.mesh.primitive_cone_add(location=(-3, 0, 0))
运行脚本时,会出现一个新圆锥。但是,如果检查 Outliner 面板,你会注意到场景中现在有三个圆锥体。额外的锥体是舞台中央的锥体的副本(相同大小,相同位置)。每次运行脚本时,你都会添加更多重复项。
为防止发生这种重复,可以添加一个循环来检查并删除场景中任何已有的网格。不再需要立方体或其代码,因此无需替换它即可。最终脚本如下所示:
import bpy
# clear meshes in the scene
for obj in bpy.data.objects:
if obj.type == 'MESH':
bpy.data.objects.remove(obj)
# add two cones
bpy.ops.mesh.primitive_cone_add()
bpy.ops.mesh.primitive_cone_add(location=(-3, 0, 0))
remove()方法从场景中移除对象。现在,每次运行脚本时,它都会在再次添加之前清除已有的网格。
四、动画
Blender的动画编程是非常强大的,可以创建结合了 Python API、强大的渲染器和模拟功能的令人惊叹的动态图像。请记住,这不是“实时渲染”方法。我们将预先定义关键帧并将它们完全渲染出来以生成形成动画的帧序列。这不是交互式的,就像你可能在Processing或某些游戏引擎中创建的那样。下面是一个基本示例,可帮助你开始使用基于现有脚本的多帧编程。
将以下代码添加到脚本的末尾:
...
# animation variables
total_frames = 100
keyframe_interval = 10
# define a one hundred frame timeline
bpy.context.scene.frame_end = total_frames
bpy.context.scene.frame_start = 0
# add keyframes
for frame in range(0, total_frames + 1, keyframe_interval):
bpy.context.scene.frame_set(frame)
cone = bpy.data.objects['Cone']
cone.location.x = frame / 25
cone.keyframe_insert(data_path='location')
有注释行(以#开头)来帮助解释每个步骤。该循环在从第 0 帧到第 100 帧 ( total_frames) 的时间线上每 10 帧 (keyframe_interval ) 插入一个新的关键帧。圆锥在关键帧之间沿 x 轴前进 0.04 个单位;Blender 将插入/补间此运动以使其平滑。
为了帮助可视化正在发生的事情,我为Dope Sheet切换了 Console 面板(在 3D 视口下方的区域中) 。可以看到每个关键帧都表示为一个黄点。按空格键开始和停止动画;蓝色播放头线的位置指示正在播放的帧:
可以决定关键帧间隔的大小。默认情况下,关键帧之间的移动是线性的——所以,实际上,这个动画只需要第 0 帧和第 100 帧上的关键帧。但我想演示如何使用循环添加更多关键帧。如果想调整动画曲线,可以使用Graph Editor进行调整。
五、波浪锥
下面脚本结合了本教程中的所有技术来生成由圆锥体制成的波浪形图案,代码如下:
import bpy
from math import sin, tau
# clear meshes in the scene
for obj in bpy.data.objects:
if obj.type == 'MESH':
bpy.data.objects.remove(obj)
# animation variables
total_frames = 150
theta = 0.0
# define a one hundred frame timeline
bpy.context.scene.frame_end = total_frames
bpy.context.scene.frame_start = 0
for x in range(30):
# generate a grid of cones
for y in range(30):
cone = bpy.ops.mesh.primitive_cone_add()
cone = bpy.context.object
cone.name = 'Cone-{}-{}'.format(x, y)
cone.location[0] = x * 2
cone.location[1] = y * 2
# add keyframes to each cone
for frame in range(0, total_frames):
bpy.context.scene.frame_set(frame)
cone.location.z = sin(theta + x) * 2 - 1
cone.keyframe_insert(data_path='location')
scale = sin(theta + y)
cone.scale = (scale, scale, scale)
cone.keyframe_insert(data_path='scale')
theta += tau / total_frames
在这种情况下,我没有使用关键帧间隔。正如Dope Sheet所示,150 帧中的每一帧都有一个单独的关键帧。我使用了一个sin()函数来生成正弦波运动;必须从math库中导入这个函数,以及tau(等于 2π)。运行脚本可能需要一段时间,具体取决于计算机的能力。可以减少每个range()函数中的30参数以加快速度。
六、关于导入的更多信息
关于导入非 Blender 模块,我们应该了解一些问题,尤其是当你将脚本拆分为多个 Python 文件或在项目中使用第 3 方包(库和模块)时。我们必须使用importlib.reload() 重新加载你导入的任何 Python 代码。例如,这是一个名为bar.py的文件,其中包含一个greeting变量:
#bar.py
greeting = 'Hello, World!'
我们首先需要将bar导入到名为foo.py的主脚本中,然后就可以使用greeting值。这是foo.py代码:
#foo.py
import bpy, os, sys, importlib
dir = os.path.dirname(bpy.data.filepath)
sys.path.append(dir)
import bar
importlib.reload(bar)
print(bar.greeting)
可以看到, .blend)文件、foo.py和bar.py都保存在同一个文件夹中;dir变量指向该位置。dir路径附加到 sys.path以便我可以导入该文件夹中的任何 python 文件。如果我更改bar.py中 greeting的值,该行将打印更新后的值——仅仅是因为我包含了print()importlib.reload(bar) 这一行。
我们可以使用 pip(Python 包安装程序)来管理第三方包,但应该使用 Blender 附带的版本。这是安装cowsay包的演示。
首先,打开终端并进入 Blender 安装目录,然后进入其中的目录(或任何适用的版本号)。现在依次输入以下命令:
python/bin/python3.7m python/lib/python3.7/ensurepip
python/bin/pip3 install cowsay --target python/lib/python3.7/
如果是Window系统,那么需要使用如下的命令:
python\bin\python.exe python\lib\ensurepip
python\bin\python.exe -m pip install cowsay --target python\lib
可以使用python/bin/pip3 list(Windows: python\bin\python.exe -m pip list )列出已安装的软件包。终端应显示如下内容:
Package Version
---------- -------
cowsay 2.0.3
pip 19.0.3
setuptools 40.8.0
如果想知道 cowsay 是做什么的,可以使用以下代码运行一个新的 Blender 脚本:
import cowsay
cowsay.cow('Blender is rad!')
接着,控制台会输出如下的内容:
_______________
< Blender is rad! >
===============
\
\
^__^
(oo)_______
(__)\ )/\
||----w |
|| ||
七、其他代码编辑器
除了Blender之外,我们可能还会使用别的编辑器,比如使用Atom来开发py脚本。如下图所示,我已经编辑了print()参数并保存了更改,这会提示 Blender 脚本编辑器显示一个解决冲突 按钮。
如果单击该按钮,则会提示从磁盘重新加载选项;这将更新 Blender 的脚本编辑器以反映我们在外部编辑器(Atom?)中所做的更改。还有一个Make text internal选项,它将脚本的 Blender 版本保存在 .blend 文件中(连同模型、材质和场景数据)。
当然,也可以直接从命令行执行脚本,而无需完全启动 Blender。但是,必须添加一些代码来呈现输出/视觉结果:
# foo.py
import bpy
bpy.ops.mesh.primitive_cone_add(location=(-3, 0, 0))
output = '/home/nuc/Desktop/render.png'
bpy.context.scene.render.filepath = output
bpy.ops.render.render(write_still=True)
该脚本将一个圆锥体添加到标准场景中,然后将其渲染到一个名为render.png(在我的桌面上)的文件中。请注意,我还将脚本保存到我的桌面。要运行它,进入到我的桌面目录并执行以下命令:
/blender --background --python foo.py
当然,需要用你的blender安装路径替换
当然,我们也可以配置代码编辑器以使用键盘快捷键(而不是终端)运行此命令。如果想要一个包含一些可以操作的数据的场景,请将 .blend 文件添加到命令中:
/blender bar.blend --background --python foo.py
更多的提升和技巧请参考:https://docs.blender.org/api/current/info_tips_and_tricks.html