Blender 是一个功能强大的开源建模平台,其功能可与 3D Studio Max 和 Maya 等专业级软件包相媲美。除了功能齐全的建模工具集之外,Blender 还具有非常强大的 Python API,它允许你创建脚本和附加组件。平台 Blender 用于建模的酷炫程度给我留下了深刻的印象……而且它是免费的!

1、Blender Python脚本环境

Blender 允许你更改视口布局以反映不同的工作方式。例如,你可能需要一组用于建模的窗口和一组用于渲染的不同窗口。脚本也是如此。Blender 带有预设的脚本布局,你可以对其进行自定义以满足你的编码需求。

Blender的界面包括:

  • 文本编辑器
  • Python 控制台
  • 信息窗口
  • Blender控制台

这是典型的脚本布局可能的样子……

2、Blender Python脚本设置

创建脚本并运行它非常容易。这一节将介绍基本工作流程以及一些在 Blender 中进入 API 的技巧。

导入 Blender Python API 是任何 Blender 脚本的第一步……你可以根据需要以标准方式导入其他库。

import bpy #Imports the Blender Python API
import mathutils #Imports Blender vector math utilities
import math #Imports the standard Python math library

print 命令会将结果打印到 Blender 控制台。你可以通过 Window 菜单或通过 Python 调用控制台来访问控制台。

#Set up some variables... standard Python rules apply...
a = 'hello'
b = ' world!'
 
#Print the addition to the system console
print (a+b)
 
#open the system console
bpy.ops.wm.console_toggle()

Blender 提供了两种探索 API 的方式:Python 控制台和信息视口。不幸的是,Blender 没有内置在代码编辑器中的代码完成功能……

Blender 的“信息”视口将所有最近的 Blender 活动显示为可执行的 Python 命令。这对于使用建模方法对流程进行原型制作然后将它们组装成脚本非常方便。

这是在场景中放置一些对象后信息窗口返回的内容……

从上面选择的文本可以复制并粘贴到文本编辑器中。我们可以从命令中删除任何不需要的选项。该脚本将在场景中创建一个网格立方体。

import bpy #Imports the Blender Python API
import mathutils #Imports Blender vector math utilities
import math #Imports the standard Python math library
 
# Create a mesh cube in the scene
bpy.ops.mesh.primitive_cube_add(location=(0.936607, -0.484878, 1.66298))

我们可以用一些变量交换位置值,以便更好地控制位置……

x = 5
y = 5
z = 0
 
# Create a mesh cube in the scene
bpy.ops.mesh.primitive_cube_add(location=(x, y, z))

…或者我们可以将命令放在 For 循环中以创建一行立方体…

for i in range (0, 5):
 
    x = i*3
    y = 0
    z = 0
 
    # Create a mesh cube in the scene
    bpy.ops.mesh.primitive_cube_add(location=(x, y, z))

或者嵌套的 For 循环用于立方体网格!

for i in range (0, 10):
    for j in range (0, 10):
 
        x = i*3
        y = j*3
        z = 0
 
        # Create a mesh cube in the scene
        bpy.ops.mesh.primitive_cube_add(location=(x, y, z))

3、Blender Python网格定义

了解如何定义和创建网格对于在 Blender 中编写几何脚本至关重要。该过程相当简单,需要用户定义以下网格属性:

  • 顶点(由 X、Y 和 Z 定义的点)
  • 边(由顶点索引定义的线框曲线)
  • 面(由顶点索引定义的 3D 表面)

在下面这个例子中,我们将定义一个简单的平面来演示顶点和面索引之间的关系。平面将由四个点组成。

首先我们定义顶点和面变量……这些被定义为数组。

import bpy
 
#Define vertices, faces
#The vertex array contains 4 items with X, Y, and Z definitions
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0)]
 
# the faces array contains 1 item.  
# The number sequence refers to the vertex array items.
# The order will determine how the face is constructed.
faces = [(0,1,2,3)]

我们现在需要为网格和场景对象定义变量……

#Define mesh and object
mymesh = bpy.data.meshes.new("Plane")
 
#the mesh variable is then referenced by the object variable
myobject = bpy.data.objects.new("Plane", mymesh)

我们现在来定义网格的创建位置。我们可以设置要在光标位置创建的网格……

#Set location and scene of object
myobject.location = bpy.context.scene.cursor_location # the cursor location
bpy.context.scene.objects.link(myobject) # linking the object to the scene

现在我们可以创建网格了……

#Create mesh
# this method has an optional 'edge' array input. This is left as an empty array
mymesh.from_pydata(verts,[],faces) 
mymesh.update(calc_edges=True) #so the edges display properly...

最终代码如下:

import bpy
 
#Define vertices and faces
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0)]
faces = [(0,1,2,3)]
 
# Define mesh and object variables
mymesh = bpy.data.meshes.new("Plane")
myobject = bpy.data.objects.new("Plane", mymesh)  
 
#Set location and scene of object
myobject.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(myobject)
 
#Create mesh
mymesh.from_pydata(verts,[],faces)
mymesh.update(calc_edges=True)

在完成上面的平面之后,立方体并没有那么大的变化。关键是跟踪顶点顺序和面索引…

import bpy
 
#Define vertices, faces, edges
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(0,0,5),(0,5,5),(5,5,5),(5,0,5)]
faces = [(0,1,2,3), (4,5,6,7), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)]
 
#Define mesh and object
mesh = bpy.data.meshes.new("Cube")
object = bpy.data.objects.new("Cube", mesh)
 
#Set location and scene of object
object.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)
 
#Create mesh
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

下面这个金字塔演示了如何使用 3 个索引而不是 4 个索引来创建三角形……

import bpy
 
#Define vertices, faces, edges
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(2.5,2.5,4.5)]
faces = [(0,1,2,3), (0,4,1), (1,4,2), (2,4,3), (3,4,0)]
 
#Define mesh and object
mesh = bpy.data.meshes.new("Cube")
object = bpy.data.objects.new("Cube", mesh)
 
#Set location and scene of object
object.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)
 
#Create mesh
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

4、Blender Python修改器

修改器使我们可以访问一些强大的网格操作功能。此页面显示如何以编程方式将修改器应用于网格。

首先看看细分修改器。

以下代码生成一个立方体...

import bpy
 
#Define vertices, faces, edges
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(0,0,5),(0,5,5),(5,5,5),(5,0,5)]
faces = [(0,1,2,3), (7,6,5,4), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)]
 
#Define mesh and object
mymesh = bpy.data.meshes.new("Cube")
myobject = bpy.data.objects.new("Cube", mymesh)
 
#Set location and scene of object
myobject.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(myobject)
 
#Create mesh
mymesh.from_pydata(verts,[],faces)
mymesh.update(calc_edges=True)

我们可以使用以下方法将细分修改器应用于网格…

# subdivide modifier
myobject.modifiers.new("subd", type='SUBSURF')

我们还可以增加可见细分的数量……

# Increase subdivisions
myobject.modifiers['subd'].levels = 3

虽然与修改器没有真正的关系......我们也可以使用以下技术将顶点着色更改为“平滑”......

# show mesh as smooth
mypolys = mymesh.polygons
for p in mypolys:
    p.use_smooth = True

完整的代码如下:

import bpy
 
#Define vertices, faces, edges
verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(0,0,5),(0,5,5),(5,5,5),(5,0,5)]
faces = [(0,1,2,3), (7,6,5,4), (0,4,5,1), (1,5,6,2), (2,6,7,3), (3,7,4,0)]
 
#Define mesh and object
mymesh = bpy.data.meshes.new("Cube")
myobject = bpy.data.objects.new("Cube", mymesh)
 
#Set location and scene of object
myobject.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(myobject)
 
#Create mesh
mymesh.from_pydata(verts,[],faces)
mymesh.update(calc_edges=True)
 
# subdivide modifier
myobject.modifiers.new("subd", type='SUBSURF')
 
# Increase subdivisions
myobject.modifiers['subd'].levels = 3
 
# show mesh as smooth
mypolys = mymesh.polygons
for p in mypolys:
    p.use_smooth = True

5、Blender Python数学网格

在掌握了使用顶点和面创建网格的基础知识之后,了解一些使用更有效的过程生成它们的技术非常重要。本页介绍了一些用于从数学方程绘制网格的代码。在此过程中,你可以看到如何使用变量和循环来组织顶点和面。

首先看波面。

这些变量对于创建波面以及让我们控制创建变化都是必不可少的。

# mesh arrays
verts = []  # the vertex array
faces = []  # the face array
 
# mesh variables
numX = 10  # number of quadrants in the x direction
numY = 10  # number of quadrants in the y direction
 
# wave variables
freq = 1  # the wave frequency
amp = 1  # the wave amplitude
scale = 1  #the scale of the mesh

定义变量后,我们可以使用它们来控制 Wave 表面的参数方程。波面用参数形式定义:

  • x = i
  • y = j
  • z = Cos(i) + Sin(j)

为了控制表面,我们可以使用我们定义的尺度、频率和幅度变量:

  • x =Scale* i
  • y =Scale* j
  • z =Scale* (Amplitude*Cos(i *Frequency) +Amplitude*Sin(j *Frequency))

通过将 x、y 和 z 变量放入包含变量 i 和 j 的嵌套 For 循环中,我们可以绘制映射参数曲面的顶点网格。

在这个阶段,我们可以通过使用顶点数组和空面数组创建网格来验证点的绘制方式。

#fill verts array
for i in range (0, numX):
    for j in range(0,numY):
 
        x = scale * i
        y = scale * j
        z = scale*((amp*math.cos(i*freq))+(amp*math.sin(j*freq)))
 
        vert = (x,y,z) 
        verts.append(vert)
 
#create mesh and object
mesh = bpy.data.meshes.new("wave")
object = bpy.data.objects.new("wave",mesh)
 
#set mesh location
object.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)
 
#create mesh from python data
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

填充完顶点数组后,我们需要填充Face数组。面数组中的每个项目都应包含 4 个索引,这些索引引用顶点数组中的一个项目。

#fill faces array
count = 0
for i in range (0, numY *(numX-1)):
    if count < numY-1:
        A = i  # the first vertex
        B = i+1  # the second vertex
        C = (i+numY)+1 # the third vertex
        D = (i+numY) # the fourth vertex
 
        face = (A,B,C,D)
        faces.append(face)
        count = count + 1
    else:
        count = 0

完整的代码如下:

import bpy
import math
 
# mesh arrays
verts = []
faces = []
 
# mesh variables
numX = 10
numY = 10
 
# wave variables
freq = 1
amp = 1
scale = 1
 
#fill verts array
for i in range (0, numX):
    for j in range(0,numY):
 
        x = scale * i
        y = scale * j
        z = scale*((amp*math.cos(i*freq))+(amp*math.sin(j*freq)))
 
        vert = (x,y,z) 
        verts.append(vert)
 
#fill faces array
count = 0
for i in range (0, numY *(numX-1)):
    if count < numY-1:
        A = i
        B = i+1
        C = (i+numY)+1
        D = (i+numY)
 
        face = (A,B,C,D)
        faces.append(face)
        count = count + 1
    else:
        count = 0
 
#create mesh and object
mesh = bpy.data.meshes.new("wave")
object = bpy.data.objects.new("wave",mesh)
 
#set mesh location
object.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(object)
 
#create mesh from python data
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

6、Blender Python随机网格

如果正在为你的脚本寻找更多的意外发现,为什么不尝试一个随机变量呢?然而,正弦波表面是一种非常可预测的形式,我们可以通过在参数图中提供随机变量来创建一个不太可预测的结构。

关键是使用 import random 导入随机库。

我们将使用与波浪表面几乎相同的脚本。但是,我们将使用随机变量,而不是使用数学 Sin 运算来控制 Z 轴。

要访问随机值,首先需要导入 random 类:

import random

然后我们可以在顶点的 For 循环中调用一个随机值……

#fill verts array
for i in range (0, numX):
    for j in range(0,numY):
 
        x = scale * i
        y = scale * j
        z = random.random()*amp
 
        vert = (x,y,z) 
        verts.append(vert)

我们可以通过将随机值乘以 i 变量并调整幅度来为随机变化创建增量效果。

#fill verts array
for i in range (0, numX):
    for j in range(0,numY):
 
        x = scale * i
        y = scale * j
        z = (i * random.random())*amp
 
        vert = (x,y,z) 
        verts.append(vert)

我们的网格有点粗糙,但我们可以使用细分修改器轻松平滑它。我们可以使用以下代码调用修改器:

# subdivide modifier
myobject.modifiers.new("subd", type='SUBSURF')
myobject.modifiers['subd'].levels = 3  # this adjusts the subdivisions in view

最后,我们可以将网格显示为“平滑”面……

# show mesh as smooth
mypolys = mymesh.polygons
for p in mypolys:
    p.use_smooth = True

完整的代码如下:

import bpy
import random
 
# mesh arrays
verts = []
faces = []
 
# mesh variables
numX = 20
numY = 20
 
# wave variables
amp = 0.5
scale = 1
 
#fill verts array
for i in range (0, numX):
    for j in range(0,numY):
 
        x = scale * i
        y = scale * j
        z = (i*random.random())*amp
 
        vert = (x,y,z) 
        verts.append(vert)
 
#fill faces array
count = 0
for i in range (0, numY *(numX-1)):
    if count < numY-1:
        A = i
        B = i+1
        C = (i+numY)+1
        D = (i+numY)
 
        face = (A,B,C,D)
        faces.append(face)
        count = count + 1
    else:
        count = 0
 
#create mesh and object
mymesh = bpy.data.meshes.new("random mesh")
myobject = bpy.data.objects.new("random mesh",mymesh)
 
#set mesh location
myobject.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(myobject)
 
#create mesh from python data
mymesh.from_pydata(verts,[],faces)
mymesh.update(calc_edges=True)
 
# subdivide modifier
myobject.modifiers.new("subd", type='SUBSURF')
myobject.modifiers['subd'].levels = 3
 
# show mesh as smooth
mypolys = mymesh.polygons
for p in mypolys:
    p.use_smooth = True

经过渲染......好了!

7、Blender Python三维超形

3D Supershape 一直是我最喜欢的数学定义。Supershapes 提供了高水平的形式变化。这个 Blender Python 实现遵循我为 The Proving Ground 为Grasshopper、Revit 和 Processing 等平台创建的先前版本。

代码如下:

import bpy
import math
 
# mesh arrays
verts = []
faces = []
edges = []
 
#3D supershape parameters
m = 14.23
a = -0.06
b = 2.78
n1 = 0.5
n2 = -.48
n3 = 1.5
 
scale = 3
 
Unum = 50
Vnum = 50
 
Uinc = math.pi / (Unum/2)
Vinc = (math.pi/2)/(Vnum/2)
 
#fill verts array
theta = -math.pi
for i in range (0, Unum + 1):
    phi = -math.pi/2
    r1 = 1/(((abs(math.cos(m*theta/4)/a))**n2+(abs(math.sin(m*theta/4)/b))**n3)**n1)
    for j in range(0,Vnum + 1):
        r2 = 1/(((abs(math.cos(m*phi/4)/a))**n2+(abs(math.sin(m*phi/4)/b))**n3)**n1)
        x = scale * (r1 * math.cos(theta) * r2 * math.cos(phi))
        y = scale * (r1 * math.sin(theta) * r2 * math.cos(phi))
        z = scale * (r2 * math.sin(phi))
 
        vert = (x,y,z) 
        verts.append(vert)
        #increment phi
        phi = phi + Vinc
    #increment theta
    theta = theta + Uinc
 
#fill faces array
count = 0
for i in range (0, (Vnum + 1) *(Unum)):
    if count < Vnum:
        A = i
        B = i+1
        C = (i+(Vnum+1))+1
        D = (i+(Vnum+1))
 
        face = (A,B,C,D)
        faces.append(face)
 
        count = count + 1
    else:
        count = 0
 
#create mesh and object
mymesh = bpy.data.meshes.new("supershape")
myobject = bpy.data.objects.new("supershape",mymesh)
 
#set mesh location
myobject.location = bpy.context.scene.cursor_location
bpy.context.scene.objects.link(myobject)
 
#create mesh from python data
mymesh.from_pydata(verts,edges,faces)
mymesh.update(calc_edges=True)
 
#set the object to edit mode
bpy.context.scene.objects.active = myobject
bpy.ops.object.mode_set(mode='EDIT')
 
# remove duplicate vertices
bpy.ops.mesh.remove_doubles() 
 
# recalculate normals
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
 
# subdivide modifier
myobject.modifiers.new("subd", type='SUBSURF')
myobject.modifiers['subd'].levels = 3
 
# show mesh as smooth
mypolys = mymesh.polygons
for p in mypolys:
    p.use_smooth = True

使用不同的参数:

#3D supershape parameters
m = 14.13
a = -0.06
b = 2.78
n1 = -2
n2 = -.1
n3 = 1
 
scale = 3
 
Unum = 70
Vnum = 70
 
Uinc = math.pi / (Unum/2)
Vinc = (math.pi/2)/(Vnum/2)
 
#fill verts array
theta = -math.pi
for i in range (0, Unum + 1):
    phi = -math.pi/2
    r1 = 1/(((abs(math.cos(m*theta/4)/a))**n2+(abs(math.sin(m*theta/4)/b))**n3)**n1)
    for j in range(0,Vnum + 1):
        r2 = 1/(((abs(math.cos(m*phi/4)/a))**n2+(abs(math.sin(m*phi/4)/b))**n3)**n1)
        x = scale * (r1 * math.cos(theta) * r2 * math.cos(phi))
        y = scale * (r1 * math.sin(theta) * r2 * math.cos(phi))
        z = scale * (r2 * math.sin(phi))
 
        vert = (x,y,z) 
        verts.append(vert)
        #increment phi
        phi = phi + Vinc
    #increment theta
    theta = theta + Uinc

原文链接:Nathan's Blender Python Notebook

BimAnt翻译整理,转载请标明出处