Spaces:
Running on Zero
Running on Zero
File size: 5,707 Bytes
94a0de3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | """生成无需外部贴图的演示场景。
blender --background --factory-startup --python scripts/create_demo.py -- --output demo.blend
"""
from __future__ import annotations
import argparse
import math
import sys
from pathlib import Path
import bpy
from mathutils import Vector
def material(name, color, metallic=0.0, roughness=0.4):
value = bpy.data.materials.new(name)
value.diffuse_color = (*color, 1)
value.use_nodes = True
shader = value.node_tree.nodes.get("Principled BSDF")
shader.inputs["Base Color"].default_value = (*color, 1)
shader.inputs["Metallic"].default_value = metallic
shader.inputs["Roughness"].default_value = roughness
return value
def smooth(obj):
for polygon in obj.data.polygons:
polygon.use_smooth = True
def aim(obj, target):
obj.rotation_euler = (Vector(target) - obj.location).to_track_quat("-Z", "Y").to_euler()
def area(name, location, energy, color, size, target=(0, 0, 1)):
bpy.ops.object.light_add(type="AREA", location=location)
obj = bpy.context.object
obj.name = name
obj.data.energy = energy
obj.data.color = color
obj.data.shape = "DISK"
obj.data.size = size
aim(obj, target)
return obj
def build(output: Path):
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.name = "Studio"
scene.render.engine = "CYCLES"
scene.cycles.samples = 64
scene.cycles.use_denoising = True
scene.render.resolution_x = 960
scene.render.resolution_y = 540
scene.render.resolution_percentage = 100
scene.render.fps = 24
scene.frame_start = 1
scene.frame_end = 120
scene.render.image_settings.file_format = "PNG"
scene.world = bpy.data.worlds.new("Studio World")
scene.world.use_nodes = True
scene.world.node_tree.nodes["Background"].inputs["Color"].default_value = (0.055, 0.085, 0.13, 1)
scene.world.node_tree.nodes["Background"].inputs["Strength"].default_value = 0.3
try:
scene.view_settings.view_transform = "AgX"
except TypeError:
pass
jade = material("Jade ceramic", (0.05, 0.42, 0.32), metallic=0.24, roughness=0.2)
gold = material("Champagne metal", (0.8, 0.55, 0.2), metallic=0.88, roughness=0.24)
cream = material("Warm porcelain", (0.76, 0.68, 0.55), roughness=0.28)
navy = material("Midnight plinth", (0.023, 0.054, 0.085), metallic=0.2, roughness=0.35)
floor = material("Studio floor", (0.055, 0.085, 0.11), roughness=0.45)
bpy.ops.mesh.primitive_plane_add(size=200)
bpy.context.object.name = "Floor"
bpy.context.object.data.materials.append(floor)
bpy.ops.mesh.primitive_cylinder_add(vertices=96, radius=1.85, depth=0.32, location=(0, 0, 0.16))
plinth = bpy.context.object
plinth.name = "Display plinth"
plinth.data.materials.append(navy)
bevel = plinth.modifiers.new("Soft edges", "BEVEL")
bevel.width = 0.08
bevel.segments = 4
smooth(plinth)
bpy.ops.mesh.primitive_monkey_add(location=(0.0, 0, 1.45))
hero = bpy.context.object
hero.name = "Jade Suzanne"
hero.rotation_euler.z = math.radians(-12)
hero.data.materials.append(jade)
subdivision = hero.modifiers.new("Sculpted surface", "SUBSURF")
subdivision.levels = 2
subdivision.render_levels = 2
smooth(hero)
bpy.ops.mesh.primitive_torus_add(major_segments=96, minor_segments=24,
location=(0, 0.25, 1.6), rotation=(math.radians(76), 0.2, 0),
major_radius=1.52, minor_radius=0.052)
ring = bpy.context.object
ring.name = "Golden orbit"
ring.data.materials.append(gold)
smooth(ring)
for name, loc, radius, mat in [
("Porcelain sphere", (1.23, -0.63, 0.63), 0.31, cream),
("Golden sphere", (-1.16, -0.67, 0.59), 0.27, gold),
("Small jade sphere", (0.7, 0.9, 0.5), 0.18, jade),
]:
bpy.ops.mesh.primitive_uv_sphere_add(segments=48, ring_count=24, radius=radius, location=loc)
obj = bpy.context.object
obj.name = name
obj.data.materials.append(mat)
smooth(obj)
area("Large warm key", (3, -4, 6), 1100, (1.0, 0.84, 0.67), 4)
area("Soft cool fill", (-4, -1, 3), 800, (0.50, 0.76, 1.0), 3)
area("Golden rim", (1, 4, 5), 1500, (1.0, 0.65, 0.28), 2.5)
bpy.ops.object.empty_add(type="PLAIN_AXES", location=(0, 0, 1.12))
target = bpy.context.object
target.name = "Camera target"
bpy.ops.object.camera_add(location=(5.4, -7.5, 4.2))
camera = bpy.context.object
camera.name = "OrbitCamera"
camera.data.lens = 52
camera.data.dof.use_dof = True
camera.data.dof.focus_object = target
camera.data.dof.aperture_fstop = 6.3
aim(camera, target.location)
track = camera.constraints.new("TRACK_TO")
track.target = target
track.track_axis = "TRACK_NEGATIVE_Z"
track.up_axis = "UP_Y"
scene.camera = camera
# 逐帧记录相机位置,不使用驱动器或自动脚本。
for frame in range(1, 121):
angle = math.radians(-68 + (frame - 1) * 70 / 119)
camera.location = (8.9 * math.cos(angle), 8.9 * math.sin(angle), 4.0)
camera.keyframe_insert(data_path="location", frame=frame)
scene.frame_set(1)
output.parent.mkdir(parents=True, exist_ok=True)
bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False)
print(f"已创建演示场景:{output.resolve()}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--output", default="demo.blend")
args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
build(Path(args.output))
|