https://sinestesia.co/blog/tutorials/python-2d-grid/
系列教程
1:生成2D 网格
4:圆角立方体
5:圆与圆柱
程序生成太棒了!在本教程系列中,我们将研究使用 Blender 的 Python API制作网格。
以编程方式创建网格提供了更多可能性。可以创建响应现实世界设置、生成艺术、基于数学的形状甚至游戏程序内容的参数对象。Blender 是此类工作的绝佳选择,因为它结合了完整的建模和动画套件以及强大的(且文档齐全的)Python API。
在本系列中,我们将着眼于制作几个基元(primitives)和一些基本的转换。
专业提示:在处理网格时经常保存!你会让 Blender 崩溃不止一次?
代码
import bpy
# 设置:名称、行数、列数、网格大小
name = 'Gridtastic'
rows = 5
columns = 10
size = 1
# 通用函数
def vert(column, row):
""" 创建一个顶点 """
return (column * size, row * size, 0)
def face(column, row):
""" 创建一个面 """
return (column* rows + row,
(column + 1) * rows + row,
(column + 1) * rows + 1 + row,
column * rows + 1 + row)
# 循环创建网格
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)]
# 创建面数据 Mesh Datablock
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(verts, [], faces)
# 创建对象,并连接到场景scene
obj = bpy.data.objects.new(name, mesh)
bpy.context.scene.collection.objects.link(obj)
# 选择该对象 blender 2.8 +
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# 选择该对象 2.8 以下
bpy.context.view_layer.objects.active = obj
obj.select = True
具体步骤
设置
Blender 数据系统区分网格数据和场景中的对象。必须添加一个网格并将其链接到一个对象,然后将该对象链接到一个场景,然后才能看到任何结果。
导入 bpy(惊喜!)并设置一些变量。
import bpy
# 设置
name = 'Gridtastic'
rows = 5
columns = 10
Name
是网格和对象的名称,而rows
和columns
控制网格有多少个顶点。
接下来设置网格和对象添加部分。先添加一个网格数据块,然后添加使用该网格的对象,最后将它们链接到当前场景。我还为顶点和面添加了一些空列表。将在稍后填写。
verts = []
faces = []
# 创建网格数据锁
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(verts, [], faces)
# 创建对象,并连接到场景scene
obj = bpy.data.objects.new(name, mesh)
bpy.context.scene.collection.objects.link(obj)
# 选择此对象
bpy.context.view_layer.objects.active = obj
最有趣的部分是。这个函数from_pydata()
从三个 python 列表创建一个网格:顶点、边和面。
请注意,如果传递面列表,可以跳过边。查看API文档以获取有关此功能的更多信息
现在运行脚本。将看到已添加了一个新对象,但看不到任何几何图形(因为尚未添加)。完成基本脚手架后,删除该对象并继续阅读以开始构建网格。
顶点网格
接下来从添加一个顶点开始。
顶点由 3 个坐标(X、Y 和 Z)表示。网格是2D的,所以只考虑 X 和 Y,Z 始终为零。
我们将把顶点放在场景的中心,这也是全局坐标的原点 (0, 0, 0)。每个顶点是 3 个浮点数的元组,因此修改列表如下:
verts = [(0, 0, 0)]
现在运行脚本,只有一个点。
来循环添加顶点。使用列表表达式很容易做到这一点:
verts = [(x, 0, 0) for x in range(columns)]
range()
返回整数数组,因此顶点 X 坐标将是它们的列号。
每列将是 1 Blender Unit(米)宽。
再次运行脚本,看到 X 轴上排列着 10 个顶点。要完成一个网格,需要更多的行。来循环遍历行:
verts = [(x, y, 0) for x in range(columns) for y in range(rows)]
了解面
我们添加的每个顶点都有一个索引号。索引是在添加顶点时设置的,第一个是 0,第二个是 1,依此类推。
制作面,需要将一组索引添加到面列表中。
这个元组可以是 3 个(tri)、4 个(quad)或更多(ngon)索引。由于制作四边形,每个面需要四个顶点索引。
先在 Blender 中启用调试模式。在 Blender 中打开 Python 控制台并输入以下内容:
bpy.app.debug = True
调试模式是个好东西。这是网格制作甚至一般插件开发中最有用的设置。
要查看顶点索引,请选择2D顶点网格并进入编辑模式。
打开 N 面板并在网格显示面板中切换“索引”。
现在您选择的任何顶点都将显示它的索引,因此选择它们以查看它们的索引。
来专注于第一个面。它由顶点 0、1、5 和 6 组成。
来使用这些索引制作一个面:
faces = [(0, 1, 5, 6)]
再次运行脚本,然后……等等,看起来有问题!连接了错误的顶点。
我们确实连接了正确的顶点,但顺序错误。
是的,设置面时需要正确顺序:从左下角开始逆时针。
因此,面的顺序是0、5、6、1。修复后:
我们需要弄清楚如何计算所有索引来制作一排面。仔细观察顶点索引,可以看到一个模式:
- 所有索引在 X 轴上递增 5。也就是行数。
- 第一个索引从 0 开始,而第二个索引从 1 开始
可以通过将当前列乘以rows
来计算循环中的第一个索引。由于第二个偏移了 1,只需加 1 即可得到。
测试下
for x in range(columns - 1):
print(x * rows)
print((x + 1) * rows)
为什么要循环。我们有 10 列顶点,但这些只创建 9 列(columns - 1
)面。最后一列没有连接到后面。
第三和第四个指标分别是(x + 1) rows + 1
和x rows + 1
。在乘以下一行的索引之前,将 X 加 1。
打印所有索引的循环:
for x in range(columns - 1):
print('first:', x * rows)
print('second:', (x + 1) * rows)
print('third:', (x + 1) * rows + 1)
print('fourth:', x * rows + 1)
print('---')
填充网格
有了所有这些知识,可以构建第一行面。
但在开始之前,将面部构建代码分离,这样可以保持代码的整洁。
我还增加了对在任何行上做面的支持。行使索引在 Y 轴上增加 1,因此可以在末尾添加行号。
def face(column, row):
""" Create a single face """
return (column* rows + row,
(column + 1) * rows + row,
(column + 1) * rows + 1 + row,
column * rows + 1 + row)
把它放到一个表达式中,就像我们对顶点所做的那样:
faces = [face(x, y) for x in range(columns - 1) for y in range(rows - 1)]
使用 rows – 1 的与使用列的原因相同。运行脚本并观察。
这样,网格完成了。已经制作了一个可以创建2D网格的脚本!在后面拍拍自己,并继续阅读以了解一些整理调整和挑战。
缩放
可以控制网格有多少个顶点,但正方形的大小总是正好 1 BU。
将 X 和 Y 坐标乘以比例因子。首先添加一个缩放变量。
可以将它直接添加到 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)]
将size设置为 1 以外的值并检查网格。
离佬真吓人