Blender 网格数据相关基础

2,920 阅读3分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

访问网格数据

Blender 中的网格数据(bpy.types.Mesh)是以 对象模式 进行访问的,用于紧凑存储,要想从 Python 中更灵活地编辑网格(编辑模式),请使用 BMesh

BMesh 这个 API 允许访问 Blender 的 内部网格编辑 API,具有几何连接数据和访问编辑操作,如分割、分离、折叠溶解 的功能。公开的特性紧跟 C API,使 Python 能够访问 Blender 自己的网格编辑工具所使用的函数。

两种 方法来访问 BMesh 数据,你可以通过从 bpy.types.BlendData.mesh 转换一个网格来创建一个新的 BMesh,或者通过访问当前的 Edit-Mode 网格。分别参见: bmesh.types.BMesh.from_meshbmesh.from_edit_mesh

Blender 存储 4 个主要数组来定义网格几何体。

  • Mesh.vertices (空间中的 3 维点)
  • Mesh.edges (引用 2 个顶点)
  • Mesh.loops (引用单个顶点和边)
  • Mesh.polygons (引用一系列 loops)

每个多边形(polygon)引用 loop 数组中的一个切片,这样,多边形不直接存储顶点或角(corner)数据,如 UV 坐标,只引用多边形使用的 loop 。

Mesh.loops, Mesh.uv_layers, Mesh.vertex_colors 都是对齐的,所以相同的多边形 loop 索引可以作为顶点用来找到 UV 坐标和顶点的颜色。

示例:访问网格几何体下的 多边形 数组 和 顶点 数组

mesh = bpy.data.meshes.new("Wave_mesh")
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

...

# 适应平滑遮蔽
mypolys = mesh.polygons
for p in mypolys:
    p.use_smooth = True
    
...    

for i in range(0,10):
    # 从顶点列表中选择一个顶点,并决定在 xyz 的哪个轴上移动。
    mesh.vertices[0].co[2] += 3
    mesh.update(calc_edges=True)
    # 将所选顶点的运动与所选轴和帧数一起插入关键帧中
    mesh.vertices[0].keyframe_insert('co',index = 2,frame = frame_num)

    frame_num += 5
    
...

注意

image.png

image.png

示例:打印每个多边形的顶点和 UV

假设活动对象是一个带有 UV 的(立方体)网格

import bpy

me = bpy.context.object.data # 默认是 bpy.data.meshes['Cube']
uv_layer = me.uv_layers.active.data

for poly in me.polygons:
    print("Polygon index: %d, length: %d" % (poly.index, poly.loop_total))

    # `range` 在这里用来显示多边形是如何引用 loops 的,其实为了方便,可以直接使用 `poly.loop_indices` 。
    for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
        print("Vertex: %d" % me.loops[loop_index].vertex_index)
        print("UV: %r" % uv_layer[loop_index].uv)

注意

bpy.data.meshes 它本身是 bpy.types.BlendDataMeshes 类型,存储的是 bpy.types.Mesh 类型

image.png

image.png

image.png

手动创建网格

Blender 还提供了我们从 顶点// 的列表中创建一个网格的接口 —— from_pydata,不过通常 可以省略,因为它可以通过顶点和面推导而出。

示例:两个面的网格

效果图

9b3b68d332c84c3e882f6c200566a34d_tplv-k3u1fbpfcp-watermark.png

代码与详细注释

import bpy

# 删除已存在的网格
for item in bpy.data.meshes:
    bpy.data.meshes.remove(item) # 从 blender 文件中移除

# 顶点数据的制作(注意顶点的坐标)
verts = [[-2.0, -2.0, 0.0], [-2.0, 2.0,  0.0], [2.0, 2.0,  0.0] , [2.0, -2.0,  0.0], [2.0, -2.0, 4.0] , [2.0, 2.0, 4.0]  ]
# 面数据的制作       
faces = [[0,1,2,3], [2,3,4,5]]

msh = bpy.data.meshes.new("cubemesh") # Mesh 数据的声明(向主数据库添加一个新网格,返回 mesh 数据块)
msh.from_pydata(verts, [], faces) # 用顶点坐标和各个面顶点的信息制作网格,边的信息可以自动推导
obj = bpy.data.objects.new("cube", msh) # 用网格数据创建对象(向主数据库添加一个新对象,返回 object 数据块)
bpy.context.scene.collection.objects.link(obj) # 在场景中放置对象

其中需要注意的是 from_pydata不会阻止 无效的网格数据(超出范围索引,匹配索引的边,双面的面等)。所以如果用于创建网格的数据不知道是否有效,则在此函数之后运行 Mesh.validate 进行验证。

示例:正弦形状的网格

效果图

c5e90476921f4fac9b770719ef7e3141_tplv-k3u1fbpfcp-watermark.png

代码与详细注释

import bpy
import math

# 删除已存在的网格
for item in bpy.data.meshes:
    bpy.data.meshes.remove(item)

# 顶点的制作
verts = []
for i in range(0,21):
    x = 2 *math.pi / 20 * i
    verts.append([x, -1, math.sin(x)]) # 某一边的顶点

for i in range(0,21):
    x = 2 * math.pi / 20 * i
    verts.append([x,  1, math.sin(x)]) # 另一边的顶点

# 面数据的制作(注意顶点的顺序)     
faces = []
for i in range(0,20):
    faces.append([i, i+1, i+22, i+21])

msh = bpy.data.meshes.new("sinmesh") # Mesh 数据的声明(向主数据库添加一个新网格,返回 mesh 数据块)
msh.from_pydata(verts, [], faces) # 用顶点坐标和各个面顶点的信息制作网格,边的信息可以自动推导
obj = bpy.data.objects.new("sin", msh) # 用网格数据创建对象(向主数据库添加一个新对象,返回 object 数据块)
bpy.context.scene.collection.objects.link(obj) # 在场景中放置对象