| 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}") |
|
|
| |
| bpy.ops.wm.open_mainfile(filepath=blend_file_path) |
|
|
| |
| bpy.context.scene.render.engine = 'CYCLES' |
|
|
| |
| bpy.context.scene.cycles.device = 'CPU' |
|
|
| |
| bpy.context.scene.cycles.samples = 4 |
|
|
| |
| 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 |
|
|
| |
| 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 |
| |
| target_name = self.alpha_axis_map.get(alpha, "单轴平面") |
| target_obj = None |
| for obj in bpy.data.objects: |
| |
| 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 |
|
|
| |
| |
| target_obj.rotation_mode = '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) |
| |
| 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") |
| |
| |
| |
|
|
|
|
|
|