from random import random
import bpy
def create_sphere(radius, distance_to_sun, obj_name):
# instantiate a UV sphere with a given
# radius, at a given distance from the
# world origin point
obj = bpy.ops.mesh.primitive_uv_sphere_add(
radius=radius,
location=(distance_to_sun, 0, 0),
scale=(1, 1, 1)
)
# rename the object
bpy.context.object.name = obj_name
# return the object reference
return bpy.context.object
N_PLANETS = 6
for n in range(N_PLANETS):
# get a random radius (a float in [1, 5])
r = 1 + random() * 4
# get a random distace to the origin point:
# - an initial offset of 30 to get out of the sun's sphere
# - a shift depending on the index of the planet
# - a little "noise" with a random float
d = 30 + n * 12 + (random() * 4 - 2)
# instantiate the planet with these parameters
# and a custom object name
create_sphere(r, d, "Planet-{:02d}".format(n))
from random import random
import bpy
def create_sphere(radius, distance_to_sun, obj_name):
# ...
def create_torus(radius, obj_name):
# (same as the create_sphere method)
obj = bpy.ops.mesh.primitive_torus_add(
location=(0, 0, 0),
major_radius=radius,
minor_radius=0.1,
major_segments=60
)
bpy.context.object.name = obj_name
return bpy.context.object
N_PLANETS = 6
for n in range(N_PLANETS):
# ...
# add the radius ring display
create_torus(d, "Radius-{:02d}".format(n))
# add the sun sphere
sun = create_sphere(12, 0, "Sun")
粗略地说, 着色器 赋予 3D 对象颜色、光泽度、光泽度、粗糙度……想想看:在现实世界中,是什么让你说“嗯,这是木头”只是一整套线索:物体有点棕色,它会反射一点光,但不会像金属那样带有尖锐的光点,它不会像玻璃那样折射光线,也不会像镜子那样反射你的图像……嗯——随着不断改进的 CGI技术,所有这些属性现在都可以在我们的 3D 场景中建模和再现!
def create_emission_shader(color, strength, mat_name):
# create a new material resource (with its
# associated shader)
mat = bpy.data.materials.new(mat_name)
# enable the node-graph edition mode
mat.use_nodes = True
# clear all starter nodes
nodes = mat.node_tree.nodes
nodes.clear()
# add the Emission node
node_emission = nodes.new(type="ShaderNodeEmission")
# (input[0] is the color)
node_emission.inputs[0].default_value = color
# (input[1] is the strength)
node_emission.inputs[1].default_value = strength
# add the Output node
node_output = nodes.new(type="ShaderNodeOutputMaterial")
# link the two nodes
links = mat.node_tree.links
link = links.new(node_emission.outputs[0], node_output.inputs[0])
# return the material reference
return mat
from math import pi
# ...
N_PLANETS = 6
START_FRAME = 1
END_FRAME = 200
for n in range(N_PLANETS):
# ...
# setup the planet animation data
planet.animation_data_create()
planet.animation_data.action = bpy.data.actions.new(name="RotationAction")
fcurve = planet.animation_data.action.fcurves.new(
data_path="rotation_euler", index=2
)
k1 = fcurve.keyframe_points.insert(
frame=START_FRAME,
value=0
)
k1.interpolation = "LINEAR"
k2 = fcurve.keyframe_points.insert(
frame=END_FRAME,
value=(2 + random() * 2) * pi
)
k2.interpolation = "LINEAR"
如果你清理场景并重新运行脚本,就会看到……什么都没有发生!即使你播放动画,行星也不会移动!
那是因为,事实上,它们是在旋转……但不是围绕正确的支点!目前,这颗行星只是在旋转,它们围绕着它们的局部 Z 轴旋转。
要解决这个问题,我们只需要更改对象的轴心点并将其捕捉回位于世界原点(与太阳相同的位置)的光标:
# ...
for n in range(N_PLANETS):
# ...
# set planet as active object
bpy.context.view_layer.objects.active = planet
planet.select_set(True)
# set object origin at world origin
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
# setup the planet animation data
# ...
现在,如果你将鼠标悬停在 3D 视图上并按下空格键,时间将开始流动,动画将开始播放,让我们的行星围绕太阳旋转!🙂
12、在脚本初始化时自动清理场景
运行脚本后,我们在场景中创建了两种类型的 资源 :行星、太阳和轨道环的 3D 对象;以及行星的动态材料。
# ...
def delete_object(name):
# try to find the object by name
if name in bpy.data.objects:
# if it exists, select it and delete it
obj = bpy.data.objects[name]
obj.select_set(True)
bpy.ops.object.delete(use_global=False)
N_PLANETS = 6
START_FRAME = 1
END_FRAME = 200
# clean scene + planet materials
delete_object("Sun")
for n in range(N_PLANETS):
delete_object("Planet-{:02d}".format(n))
delete_object("Radius-{:02d}".format(n))
for m in bpy.data.materials:
bpy.data.materials.remove(m)
# ...
# ...
def find_3dview_space():
# find the 3D view panel and its screen space
area = None
for a in bpy.data.window_managers[0].windows[0].screen.areas:
if a.type == "VIEW_3D":
area = a
break
return area.spaces[0] if area else bpy.context.space_data
def setup_scene():
# (set a black background)
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value = (0, 0, 0, 1)
# (make sure we use the EEVEE render engine + enable bloom effect)
scene = bpy.context.scene
scene.render.engine = "BLENDER_EEVEE"
scene.eevee.use_bloom = True
# (set the animation start/end/current frames)
scene.frame_start = START_FRAME
scene.frame_end = END_FRAME
scene.frame_current = START_FRAME
# get the current 3D view (among all visible windows
# in the workspace)
space = find_3dview_space()
# apply a "rendered" shading mode + hide all
# additional markers, grids, cursors...
space.shading.type = 'RENDERED'
space.overlay.show_floor = False
space.overlay.show_axis_x = False
space.overlay.show_axis_y = False
space.overlay.show_cursor = False
space.overlay.show_object_origins = False
N_PLANETS = 6
START_FRAME = 1
END_FRAME = 200
# setup scene settings
setup_scene()
# ...
# deselect all objects
bpy.ops.object.select_all(action='DESELECT')
14、结束语
Blender 不是唯一允许你对场景进行编程和自动执行任务的 3D 软件;但它不负众望,随着每个新版本的推出,Blender 逐渐成为一个 可靠的一体化 CG 制作解决方案,从使用油性铅笔的故事板到基于节点的合成。