blender-render-studio / scripts /create_demo.py
cpuai's picture
Deploy Blender Render Studio with Gradio API and MCP
94a0de3 verified
Raw History Blame Contribute Delete
5.71 kB
"""生成无需外部贴图的演示场景。
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))