这是从Blender官方教程目录中系统整理的BlenderProc API核心用法指南,涵盖了场景搭建、材质应用、相机设置及渲染输出等关键步骤的代码示例与参数详解。
Loader
CLI下载外部资源
blenderproc download <dataset> <output_dir>
支持的外部资源有许多,包括blenderkit中CC0 许可的模型与材质、CC0textures的高质量PBR纹理、polyhaven的许多环境光与模型以及一些直接的数据集中的资源(matterport3d,pix3d,scenenet)等。注意有些单独的.blend文件是允许下载的,但是无法通过CLI来逐个下载,可能只能下载特定的数据集。
加载3D模型
所有加载函数都属于bproc.loader模块,返回类型是list[bproc.types.MeshObject],通用的调用格式为:
objects: list[MeshObject] = bproc.loader.load_xxx(<path_or_config>)
# bproc.loader.load_obj(file_path)
# bproc.loader.load_blend(file_path)
# 比如下载一个chair.obj文件
objs = bproc.loader.load_obj("/assets/chair.obj")
chair = objs[0] # 获取第一个对象
还有一种是加载专用数据集的loaderAPI,具体用法如下,同时以BOP_obj为例(包括AMASS,pix3D,scung等都可以用)
bproc.loader.load_<dataset>(dataset_features)
bproc.loader.load_bop_objs(bop_dataset_path, model_type, category_ids)
# 记载BOP中的T-LESS模型
models = bproc.loader.load_bop_objs(
bop_dataset_path="/data/bop/tless",
model_type="cad",
category_ids=["01", "02"]
)
操作加载完成的模型对象
每个加再多物体都是一个MeshObject实例,可以实现位姿变换
obj.set_location([x,y,z])
obj.set_rotation_euler([rx, ry, rz]) #这里是设置旋转角度,绕三个轴
obj.set_local2world_mat(matrix_4x4) #设置局部空间到世界坐标的变换矩阵
obj.apply_T(matrix_4x4) #在当前位姿基础上叠加变换
import numpy as np
obj.set_location([2.0, 0.0, 1.0])
# 绕 X 轴旋转 180°
obj.set_rotation_euler([np.pi, 0, 0])
# 构建变换矩阵(从旋转矩阵R_3x3和平移矩阵T_3x1堆叠)
T = bproc.math.build_transformation_mat(
location=[0, 0, 0.1],
rotation_euler=[0, np.pi/4, 0]
)
obj.apply_T(T)
给对象自定义属性,set和get方法
obj.set_cp(key: str, value: Any)
obj.get_cp(key: str, default=None) #键不存在则返回默认值
# 以chair为例
obj.set_cp("category", "chair")
obj.set_cp("instance_id", 42)
obj.set_cp("is_target", True)
# 读取标签
cat = obj.get_cp("category") # "chair"
id_ = obj.get_cp("instance_id") # 42
color = obj.get_cp("color", "white") # "white"(默认值)
Camera Configuration
相机内参
一般情况下包含两个方向焦距以及主点,表示为
bproc有两种方式设置内参,第一种是直接设置K
bproc.camera.set_intrinsics_from_K_matrix(K, image_width, image_height)
# 用法为
K = np.array([
[800, 0, 400], # fx, 0, cx
[0, 800, 300], # fy, cy
[0, 0, 1]
])
# 图像分辨率为 800x600
bproc.camera.set_intrinsics_from_K_matrix(K, image_width=800, image_height=600)
第二种是设置物理参数,这种需要进行计算,为了内容完整性还是写出(注意这里有一些合理性假设例如$f_x=f_y$)
bproc.camera.set_intrinsics_from_blender_params(
lens=value,
lens_unit="MILLIMETERS" # 设定为"MILLIMETERS"表示输入的lens值是以毫米为单位的焦距
)
bproc.camera.set_intrinsics_from_blender_params(lens=math.pi/3, lens_unit="FOV") #设置水平视场角为 60°(≈ π/3 弧度)
#视场角(Field of View, FOV):以弧度为单位指定水平视角宽度
相机外参
定义相机在世界坐标系的位置和朝向,相当于从相机坐标系到世界坐标系的变换矩阵
bproc.camera.add_camera_pose(cam2world_matrix)
# 构造一个 4x4 变换矩阵(相机在 [0, -2, 1],看向原点)
tmat = bproc.math.build_transformation_mat(
location=[0, -2, 1],
rotation_euler=[0, math.radians(80), 0]
)
# 添加相机位姿
bproc.camera.add_camera_pose(tmat)
需要注意的是每调用一次 add_camera_pose,就会添加一个关键帧(keyframe),渲染时会从该视角生成一张图像
此外还有从OpenCV坐标系转换到OpenGL坐标系的用法,Blender使用OpenGL坐标系,与OpenCV的Z坐标一个向内一个向外,Y坐标一个向上一个向下,因此OpenCV 的相机到世界变换矩阵(cam2world)不能直接用于 Blender,需要进行转换
# 将 OpenCV 格式的 cam2world 矩阵转换为 Blender/OpenGL 兼容格式
cam2world_opengl = bproc.math.change_source_coordinate_frame_of_transformation_matrix(
transformation_matrix=cam2world_opencv,
source_frame=["X", "-Y", "-Z"] # OpenCV → OpenGL
)
bproc.camera.add_camera_pose(cam2world_gl)
Sampler
BlenderProc 提供了强大的 bproc.sampler模块,支持多种空间采样策略,可以生成更多样化的布局
| 采样器 | 方法 | 用途 |
|---|---|---|
| 球面采样 | sampler.sphere() | 相机环绕物体(固定距离) |
| 壳状采样 | sampler.shell() | 相机环绕物体(随机距离) |
| 半球采样 | sampler.hemisphere() | 上半空间采样 |
| 圆柱采样 | sampler.cylinder() | 工业场景(如机械臂视角) |
| 网格采样 | sampler.grid() | 规则网格路径(如无人机航拍) |
| 表面采样 | sampler.surface() | 在平面上随机分布物体 |
| 体积采样 | sampler.volume() | 在空间区域内随机分布 |
这里只详细说明壳状采样,其他类似
cam2world = bproc.sampler.shell(
center=[0, 0, 0], # 采样中心点
radius_min=2.0, # 最小半径
radius_max=3.0, # 最大半径
mode="SURFACE" # 采样模式:SURFACE(球壳表面) 或 VOLUME(整个球体内部)
)
Render
帧区间
BlenderProc的渲染是基于帧序列的,当调用渲染函数时,他会渲染$[frame_{start},frame_{end})$的所有帧,其中每一帧对应一个camera pose,可以用如下方式设置渲染帧范围
bproc.global_settings.set_render_range(frame_start=1, frame_end=3) # 渲染第1帧和第2帧
RGB渲染器(主渲染)
最常用的方式是
data = bproc.renderer.render()
其中render()会返回一个dict,包含启用的所有图像类型
{
"colors": [
<np.uint8: [512, 512, 3]>, // 第1帧 RGB
<np.uint8: [512, 512, 3]> // 第2帧 RGB
],
"normals": [
<np.float32: [512, 512, 3]>, // 第1帧法线
<np.float32: [512, 512, 3]>
],
"distance": [
<np.float32: [512, 512]>, // 第1帧距离图
<np.float32: [512, 512]>
]
}
深度图和距离图
首先是距离图,是distance,像素的distance值=相机位置到3D点的真实欧氏距离,可用于3D重建和点云生成
其次是深度图,即depth/z-buffer,像素的深度值=3D点在相机Z轴上的投影距离
一般用到的是distance,两者的用法如下
bproc.renderer.enable_distance_output()
bproc.renderer.enable_depth_output()
法线图
法线渲染器会输出每个像素对应表面的法线向量(在世界坐标系下)
bproc.renderer().enable_normals_output()
# 输出类型:np.float32,形状 [H, W, 3],值范围是[-1,1],表示法线方向
分割渲染
用于标注图像中每个像素属于哪个物体
data = bproc.renderer.render_segmap(map_by=["instance", "class", "name"])
# instance即实例分割,为每个物体分配唯一ID
# class即语义分割,使用物体的category_id属性
输出的数据类似
{
"instance_segmaps": [...], // 实例分割图
"class_segmaps": [...], // 类别分割图
"instance_attribute_maps": [
[ // 每帧一个映射表
{"idx": 0, "name": "chair_01"},
{"idx": 1, "name": "lamp_02"}
],
[...] // 下一帧(映射保持一致)
]
}
可以用上面的set_cp来设置物体类别ID
obj = bproc.loader.load_obj("chair.obj")
obj.set_cp("category_id", 3) # 类别 3 = 椅子
光流渲染器
光流是来描述像素在连续的帧之间的运动方向和大小的
data = bproc.renderer.render_optical_flow()
采样去噪
控制每个像素的采样数,知道噪声低于设置的阈值,同时也可以限制最大采样次数
bproc.renderer.set_noise_threshold(0.05) # 值越小越清晰越慢,0.0~0.1
bproc.renderer.set_max_amount_of_samples(512)
去噪器可以大幅减少所需的采样数量,官方文档中暂时给出了启用Intel和关闭去噪的用法
# 启用 Intel Open Image Denoiser
bproc.renderer.set_denoiser("INTEL")
# 关闭去噪
bproc.renderer.set_denoiser(None)
Writer
完成render渲染后,需要把生成的数据用于训练模型,BlenderProc提供了多种Writer模块,如HDF5,COCO,BOP等
不管用哪种Writer,基本流程都是一样的,所有 Writer 都会自动创建目录并组织文件结构
首先是hdf5,每个.hdf5是一个包含colors,distance,instance_segamp,camera_intrinsics,camera_rt_matrix等的类似json文件
其次是COCO,多用于目标检测和实例分割,使用起来的参数较多,见下代码(举的例子是实例分割)
最后是BOP,多用于6D位姿估计。用法见下
# 1. 渲染数据
data = bproc.renderer.render()
# 2. 调用对应的 writer
bproc.writer.write_hdf5("/output/hdf5", data)
bproc.writer.write_coco_annotations(
data=data,
instance_segmaps=data["instance_segmaps"], # 提前渲染
dataset_name="my_dataset",
supercategory="scene",
images_dir="/output/coco/images",
annotations_dir="/output/coco/annotations"
)
bproc.writer.write_bop(
output_dir="/output/bop",
camera_settings={
"depth_scale": 1.0, # 深度图缩放因子
"intrinsics": K # 相机内参矩阵
},
dataset="my_bop_dataset",
frames_data=data,
save_world2cam_transforms=True
)
最后是可视化数据
blenderproc vis hdf5 /output/hdf5/0.hdf5
blenderproc vis coco /output/coco/annotations/my_dataset.json
Keyframes
关键帧可以批量渲染,适合大规模的合成数据集生成,网格只上传一次到GPU,每增加一次相机位姿会自动添加一个关键帧
for i in range(100):
bproc.camera.add_camera_pose(pose_i) # 添加相机位姿,即添加关键帧
bproc.renderer.render() # 一次性渲染
在调用bproc.camera.add_camera_pose()时,关键帧会自动递增编号,也可以在该方法后加一个frame=k来显示绑定该相机位姿到帧k
同时注意如果添加多个相机位姿之后再调用obj.set_location(),就会重置所有帧的物体位置,可能导致出错,同样可以在该方法后面加一个frame参数来为特定帧设定物体位置
obj.set_location([2, 0, 0], frame=1) #在第一帧时物体位置设定为2,0,0
如果在同一个文件中多次调用renderer,必须重置关键帧,不然关键帧会一直累积,渲染的帧数会越来越多
Rigid Physics
总览
为了防止出现穿模或悬空等情况,BlenderProc集成了刚体物理引擎,可以模拟重力
对一个需要参与物理模拟的物体启用刚体组件,例如:
obj.enable_rigidbody(
active=True, # 是否主动移动(受重力)
mass=1.0, # 质量(kg)
friction=0.5, # 摩擦系数
collision_shape="CONVEX_HULL" # 碰撞形状
)
table.enable_rigidbody(active=False, collision_shape="MESH")
其中,active参数为True表示物体受重力影响,会移动,为False表示物体是固定不动作为障碍物的,例如墙壁地板等
collision_shape表示碰撞形状,直接影响模拟稳定性和计算效率,CONVEX_HULL表示简单的凸形物体,例如球和立方体,忽略其凹陷部分;MESH表示复杂非凸物体,例如齿轮等,渲染不太稳定;COMPOUND为有高精度需求的非凸物体。
Convex_decomposition
如上文所述,如果对一些非凸物体(椅子)直接使用MESH碰撞形状容易导致物体穿模或抖动,因此BlenderProc内置了V-HACD来进行凸分解
obj.build_convex_decomposition_collision_shape(
vhacd_path="/tmp/vhacd" # 存储分解结果的目录
)
原始物体会被分解为多个凸块,渲染时不可见,仅用于物理碰撞检测,在模拟结束后会自动清理
物理模拟
BlenderProc有两种模拟的模式,首先是静态摆放,它模拟整个下落过程,但是只会保留最终的静止姿态,然后清除动画,也就是说最后生成的是单帧稳定场景
bproc.object.simulate_physics_and_fix_final_poses(
min_simulation_time=4, # 最少模拟 4 秒
max_simulation_time=20, # 最多模拟 20 秒
check_object_interval=1 # 每 1 秒检查是否静止
)
# 随机设置多个物体随机初始位置
for obj in objects:
obj.set_location(np.random.uniform([-0.5, -0.5, 2], [0.5, 0.5, 3]))
obj.enable_rigidbody(active=True, collision_shape="CONVEX_HULL")
# 模拟物理,固定最终位置
bproc.object.simulate_physics_and_fix_final_poses()
# 添加相机并渲染
for i in range(5):
pose = bproc.sampler.shell(center=[0,0,0], radius_min=2, radius_max=3)
bproc.camera.add_camera_pose(pose)
data = bproc.renderer.render()
bproc.writer.write_hdf5("/output/scene_001", data)
第二种方法是动态渲染,这个会保留动画,可以用作训练视频模型,暂时没有深入整理
bproc.object.simulate_physics(
min_simulation_time=3,
max_simulation_time=10,
check_object_interval=1
)