import bpy import math import os from paths import * class BlendRenderer: def __init__(self, blend_file_path=RENDER_FILE): """ 初始化渲染器,加载指定的 .blend 文件并进行基础设置。 :param blend_file_path: 要加载的 .blend 文件的完整路径 """ if not os.path.isfile(blend_file_path): raise FileNotFoundError(f"Blend file not found: {blend_file_path}") # 加载 blend 文件 bpy.ops.wm.open_mainfile(filepath=blend_file_path) # 设置渲染引擎为 Cycles bpy.context.scene.render.engine = 'CYCLES' # 使用 CPU 渲染 bpy.context.scene.cycles.device = 'CPU' # 设置采样数为 4 bpy.context.scene.cycles.samples = 4 # 设置所有反弹次数为 4(包括 diffuse, glossy, transmission, etc.) bpy.context.scene.cycles.max_bounces = 4 # 设置渲染分辨率 bpy.context.scene.render.resolution_x = 512 bpy.context.scene.render.resolution_y = 512 bpy.context.scene.render.resolution_percentage = 100 # 启用透明背景(RGBA) bpy.context.scene.render.film_transparent = True # 遍历所有对象,初始化渲染可见性 for obj in bpy.data.objects: if obj.type == 'LIGHT': obj.hide_render = False elif obj.type == 'CAMERA': obj.hide_render = False elif obj.type == 'MESH': obj.hide_render = True # 默认所有网格不参与渲染 # 设置活动摄像机(选第一个) cameras = [obj for obj in bpy.data.objects if obj.type == 'CAMERA'] if cameras: bpy.context.scene.camera = cameras[0] print(f"Loaded blend file: {blend_file_path}") print("Render settings applied: 512x512, CPU, samples=4, bounces=4, transparent background.") self.alpha_axis_map = { 0: "单轴平面", 1: "三轴", 2: "双向标注", 4: "四向标注" } def _get_all_children(self, obj): """递归获取对象的所有子对象(包括嵌套子级)""" children = [] for child in obj.children: children.append(child) children.extend(self._get_all_children(child)) return children def render_axis(self, azi, ele, rot, alpha, save_path): """ 渲染特定方向的图像。 :param azi: 方位角(绕 Z 轴旋转,弧度) :param ele: 仰角(绕 Y 轴旋转,弧度) :param rot: 自转(绕 X 轴旋转,弧度) :param save_path: 渲染结果保存路径(如 '/output/render.png') """ # 遍历所有对象,初始化渲染可见性 for obj in bpy.data.objects: if obj.type == 'LIGHT': obj.hide_render = False elif obj.type == 'CAMERA': obj.hide_render = False elif obj.type == 'MESH': obj.hide_render = True # 默认所有网格不参与渲染 # 根据 alpha 选择目标对象 target_name = self.alpha_axis_map.get(alpha, "单轴平面") target_obj = None for obj in bpy.data.objects: # if obj.type == 'MESH' and obj.name == target_name: if obj.name == target_name: target_obj = obj break if target_obj is None: raise ValueError(f'Object named "{target_name}" not found in the scene.') # 获取该对象及其所有子对象 all_objects_to_render = [target_obj] + self._get_all_children(target_obj) # 设置它们参与渲染 for obj in all_objects_to_render: if obj.type == 'MESH': obj.hide_render = False # 设置旋转(ZYX 顺序:Z=azi, Y=ele, X=rot → Euler XYZ = (rot, ele, azi)) # 注意:Blender 使用弧度 target_obj.rotation_mode = 'ZYX' # 确保使用欧拉角 ZYX 模式 target_obj.rotation_euler = (rot*math.pi/180, ele*math.pi/180, -azi*math.pi/180) # 确保路径目录存在 os.makedirs(os.path.dirname(save_path), exist_ok=True) # 设置输出路径 bpy.context.scene.render.filepath = save_path # 执行渲染并保存 bpy.ops.render.render(write_still=True) print(f"Rendered and saved to: {save_path}") if __name__ == "__main__": renderer = BlendRenderer(RENDER_FILE) # Example usage: renderer.render_axis(45, 0, 0, 1, "./test_demo_output/render_1_dir_azi45.png") renderer.render_axis(0, 45, 0, 2, "./test_demo_output/render_2_dir_ele45.png") renderer.render_axis(0, 0, 45, 4, "./test_demo_output/render_4_dir_rot45.png") # renderer.render_1_dir() # renderer.render_2_dir() # renderer.render_4_dir()