"""hakobu.blend から確認用の絵を書き出す(Cycles・CPU)。 使い方: python3 hakobu_render.py <出力フォルダ> [views...] [--drawer-open 0.28] views: front side back persp sim sim_open top """ import math import os import sys import bpy # noqa: I001 from mathutils import Vector def setup(samples=24): sc = bpy.context.scene sc.render.engine = "CYCLES" sc.cycles.device = "CPU" sc.cycles.samples = samples sc.cycles.use_denoising = True try: sc.cycles.denoiser = "OPENIMAGEDENOISE" except Exception: pass sc.view_settings.view_transform = "Standard" sc.render.film_transparent = False world = bpy.data.worlds.new("W") sc.world = world world.use_nodes = True bg = world.node_tree.nodes["Background"] bg.inputs["Color"].default_value = (0.93, 0.93, 0.92, 1) bg.inputs["Strength"].default_value = 0.9 sun = bpy.data.lights.new("Sun", "SUN") sun.energy = 1.8 sun.angle = math.radians(25) so = bpy.data.objects.new("Sun", sun) so.rotation_euler = (math.radians(50), math.radians(-15), math.radians(-35)) sc.collection.objects.link(so) return sc def floor(sc, size=8, color=(0.80, 0.80, 0.78)): me = bpy.data.meshes.new("Floor") s = size / 2 me.from_pydata([(-s, -s, 0), (s, -s, 0), (s, s, 0), (-s, s, 0)], [], [(0, 1, 2, 3)]) ob = bpy.data.objects.new("Floor", me) mat = bpy.data.materials.new("FloorMat") mat.use_nodes = True mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (*color, 1) mat.node_tree.nodes["Principled BSDF"].inputs["Roughness"].default_value = 0.8 me.materials.append(mat) sc.collection.objects.link(ob) return ob def person(sc, h=1.60, x=0.62, y=0.05): """大きさの目安の人(身長1.60m)""" mat = bpy.data.materials.new("PersonMat") mat.use_nodes = True mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = (0.42, 0.62, 0.78, 1) bpy.ops.mesh.primitive_cylinder_add(radius=0.17, depth=h - 0.24, location=(x, y, (h - 0.24) / 2)) body = bpy.context.active_object bpy.ops.mesh.primitive_uv_sphere_add(radius=0.11, location=(x, y, h - 0.11)) head = bpy.context.active_object for o in (body, head): o.data.materials.append(mat) o.name = "Person" def camera(sc, loc, target, ortho=None, lens=50): cam = bpy.data.cameras.new("Cam") if ortho: cam.type = "ORTHO" cam.ortho_scale = ortho else: cam.lens = lens co = bpy.data.objects.new("Cam", cam) co.location = loc d = Vector(target) - Vector(loc) co.rotation_euler = d.to_track_quat("-Z", "Y").to_euler() sc.collection.objects.link(co) sc.camera = co return co def main(): args = sys.argv[1:] blend, out = args[0], args[1] views = [a for a in args[2:] if not a.startswith("--")] bpy.ops.wm.open_mainfile(filepath=blend) sc = setup() H = max((ob.matrix_world @ Vector(c)).z for ob in bpy.data.objects if ob.type == "MESH" for c in ob.bound_box) os.makedirs(out, exist_ok=True) fl = None for v in views or ["front", "side", "back", "persp"]: for ob in list(bpy.data.objects): if ob.type == "CAMERA": bpy.data.objects.remove(ob) for o in bpy.data.objects: if o.name.startswith(("Hakobu_Drawer", "Hakobu_Tray")): o.location.y = -0.26 if v.endswith("_open") else 0.0 if v.startswith("sim") and fl is None: fl = floor(sc, color=(0.74, 0.66, 0.55)) person(sc) if v == "people" and fl is None: fl = floor(sc, color=(0.74, 0.66, 0.55)) person(sc) if fl: fl.hide_render = not (v.startswith("sim") or v == "people") for o in bpy.data.objects: if o.name.startswith("Person"): o.hide_render = not (v.startswith("sim") or v == "people") m = H * 1.12 if v == "front": camera(sc, (0, -5, H / 2), (0, 0, H / 2), ortho=m) sc.render.resolution_x, sc.render.resolution_y = 640, 1000 elif v == "side": camera(sc, (5, 0, H / 2), (0, 0, H / 2), ortho=m) sc.render.resolution_x, sc.render.resolution_y = 640, 1000 elif v == "back": camera(sc, (0, 5, H / 2), (0, 0, H / 2), ortho=m) sc.render.resolution_x, sc.render.resolution_y = 640, 1000 elif v == "top": camera(sc, (0, 0, 5), (0, 0.0001, 0), ortho=m * 0.7) sc.render.resolution_x, sc.render.resolution_y = 800, 800 elif v == "persp": camera(sc, (1.25, -2.3, 1.25), (0, 0, H * 0.48), lens=55) sc.render.resolution_x, sc.render.resolution_y = 800, 1000 elif v == "face": camera(sc, (0, -3, H * 0.84), (0, 0, H * 0.84), ortho=0.42) sc.render.resolution_x, sc.render.resolution_y = 800, 700 elif v == "people": camera(sc, (1.9, -4.2, 1.35), (0.3, 0, 0.75), lens=50) sc.render.resolution_x, sc.render.resolution_y = 1000, 800 elif v.startswith("sim"): # シミュレーションの寄り(左30°・75°見下ろし)に近い角度 dist = 3.2 el = math.radians(75) az = math.radians(-90 - 30) loc = (dist * math.cos(el) * math.cos(az), dist * math.cos(el) * math.sin(az), dist * math.sin(el)) camera(sc, loc, (0, -0.05, H * 0.35), lens=45) sc.render.resolution_x, sc.render.resolution_y = 1000, 800 sc.render.filepath = os.path.join(out, f"render_{v}.png") bpy.ops.render.render(write_still=True) print("wrote", sc.render.filepath) if __name__ == "__main__": main()