Orient-Anything-V2 / axis_renderer.py
zhangziang
initial commit track binary
f783161
raw
history blame
4.95 kB
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()