引言

计算机专业硕士在读,主要研究方向是特定目标大斜视角目标检测与定位。因为要做的是特定目标,公开数据集较少,经过多方考虑还是决定要自建数据集。最终考虑的解决方案还是 Blender + Python API 的方式,项目起名叫 RealEarthStudio
这系列文章主要对开发过程进行记录,方便我个人后续查看,也给相类似方向的同学提供一个思路。

【项目目录】:项目目录链接


一、主要功能

  • 功能:使用Blender的PythonAPI实现对目标的环绕拍摄。
  • 背景:上一篇文章已经实现了对光照的控制,现在我希望能够创建摄像机对目标进行拍摄并保存标注信息。这里需要说明的是,由于后续我需要做大斜视角目标检测相关内容,所以需要在确定的仰角下对目标进行拍摄,初步考虑是将该拍摄功能设计为函数形式,方便后续调用。
  • 效果
    在这里插入图片描述

二、实现步骤

2.1 根据相对位置计算目标遮挡率并输出标注信息

(1)功能需求

  • 根据当前设置好的相机参数和模型信息对目标遮挡情况进行判定
  • 剔除不可见的图像(这里我设置的是 遮挡率>80% 即为不可见)
  • 输出标注信息

(2)方法研究

使用Blender的PythonAPI进行遮挡情况分析主要有以下几种方式:

方法 1:深度图比对法(Z-Buffer)

方法原理

  • 渲染一张仅包含目标物体的深度图(depth_target)
  • 渲染一张完整场景的深度图(depth_scene)
  • 对每个像素比较:若 depth_scene < depth_target,说明该像素处有其他物体更靠近相机 → 目标被遮挡

方法 2:射线投射法(Ray Casting)

方法原理

  • 在目标物体表面采样若干点(顶点或面中心)
  • 从相机向这些点发射射线
  • 如果射线在到达目标点前击中其他物体 → 该点被遮挡

方法 3:ID Mask法(对象 ID 渲染)

方法原理

  • 启用 Object Index Pass
  • 给目标物体设置唯一 pass_index
  • 渲染后检查图像中该 ID 的像素占比

方法 4:Bounding Box 投影重叠法(粗略估计)

方法原理

  • 投影目标物体和所有其他物体的 bbox 到图像平面
  • 计算 2D bbox 重叠区域
  • 重叠比例 ≈ 遮挡比例(非常粗略)

各方法对比总结:

方法 精度 速度 实现难度 适用场景
深度图比对 ⭐⭐⭐⭐⭐ 慢(2次渲染) 高质量数据集生成
射线投射 ⭐⭐⭐☆ 批量处理、实时检测
ID Mask ⭐⭐⭐⭐ 中(1次渲染+图像处理) 需要像素级可见性
BBox 重叠 极快 快速过滤、预筛

最后考虑到性能需求,选择射线投射法对目标遮挡情况进行检测。检测完毕后剔除遮挡率>80%的图像,并生成对应的标注信息。

(3)最终实现

该函数如下:

def get_visible_info(camera, target_obj, scene, occlusion_threshold=0.8, sample_rate=0.1):
    """
    使用射线投射快速判断目标是否可见(遮挡比例 <= threshold)
    返回: (is_visible: bool, occlusion_ratio: float, bbox: (cx,cy,w,h) or None)
    """
    cam_loc = camera.matrix_world.translation
    depsgraph = bpy.context.evaluated_depsgraph_get()

    # 获取目标顶点(世界坐标)
    eval_obj = target_obj.evaluated_get(depsgraph)
    mesh = eval_obj.to_mesh()
    vertices_world = [target_obj.matrix_world @ v.co for v in mesh.vertices]
    eval_obj.to_mesh_clear()

    if not vertices_world:
        return False, 1.0, None

    # 随机采样
    num_verts = len(vertices_world)
    sample_count = max(50, int(num_verts * sample_rate))
    indices = np.random.choice(num_verts, size=min(sample_count, num_verts), replace=False)
    sampled_points = [vertices_world[i] for i in indices]

    visible_2d = []
    occluded = 0

    for pt in sampled_points:
        direction = (pt - cam_loc).normalized()
        # 射线投射(忽略目标自身)
        result, location, normal, index, hit_obj, matrix = scene.ray_cast(
            depsgraph, cam_loc, direction, distance=(pt - cam_loc).length - 1e-4
        )

        if not result or hit_obj == target_obj:
            # 无遮挡,或仅击中自己(视为可见)
            co_2d = world_to_camera_view(scene, camera, pt)
            if 0 <= co_2d.x <= 1 and 0 <= co_2d.y <= 1 and co_2d.z > 0:
                visible_2d.append((co_2d.x, co_2d.y))
        else:
            occluded += 1

    total = len(sampled_points)
    if total == 0:
        return False, 1.0, None

    occlusion_ratio = occluded / total
    is_visible = occlusion_ratio <= occlusion_threshold

    # 计算 bbox(仅基于可见点)
    if visible_2d:
        xs = [p[0] for p in visible_2d]
        ys = [p[1] for p in visible_2d]
        x_min, x_max = min(xs), max(xs)
        y_min, y_max = min(ys), max(ys)
        cx = (x_min + x_max) / 2
        cy = 1 - (y_min + y_max) / 2
        w = x_max - x_min
        h = y_max - y_min
        bbox = (float(cx), float(cy), float(w), float(h))
    else:
        bbox = None

    return is_visible, occlusion_ratio, bbox

【输入参数】
camera:摄像机对象
target_obj:目标对象
scene:场景对象
occlusion_threshold:遮挡阈值(超出80%即为不可见)
sample_rate:采样率

【输出参数】
is_visible:(布尔类型)是否可见
occlusion_ratio:(浮点类型)遮挡率
bbox:(cx,cy,w,h)标注框信息

2.2 高效的渲染图像(GPU加速)

(1)功能需求

  • 在使用 CYCLES 渲染引擎时,能够通过GPU加速

(2)方法研究

Blender内置的渲染引擎主要是 EEVEECYCLES 两种,简单总结就是:

引擎 一句话总结
Eevee “所见即所得”的实时创作利器,适合快速迭代与风格化表达
Cycles “以时间换真实”的物理级渲染专家,适合最终高质量输出

以下是他们在核心定位、技术原理、性能效率、画质能力等方面的对比:

特性 Eevee Cycles
类型 实时光栅化渲染器(Real-time Rasterizer) 离线路径追踪渲染器(Offline Path Tracer)
灵感来源 游戏引擎(如 Unreal Engine) 物理光学模拟(如电影级渲染器)
目标 速度 + 交互性 物理真实感
典型用途 动画预览、风格化作品、VR/AR、游戏资产 影视特效、产品可视化、建筑表现、照片级静帧
渲染方式 光栅化(Rasterization)
→ 逐三角形绘制,使用“视觉技巧”模拟光照
路径追踪(Path Tracing)
→ 模拟光线在场景中反弹、折射、散射的物理过程
全局光照 (GI) 近似模拟:
• 烘焙 Light Probes
• Irradiance Volumes
• 屏幕空间反射(SSR)
自动计算:
• 完整 GI
• 多次反弹
• 能量守恒
阴影 阴影贴图(Shadow Maps)
→ 可能出现锯齿、自遮挡错误
光线追踪阴影
→ 柔和、精确、支持透明/半透明物体
反射/折射 屏幕空间(SSR)+ 烘焙探针
→ 无法反射屏幕外物体
全局光线追踪
→ 支持无限次反射、焦散(Caustics)、精确折射
材质 基于 Principled BSDF,但部分效果需手动补偿 完全基于物理的材质系统,自动处理复杂光学行为
渲染速度 极快(实时,60+ FPS)
→ 视口即最终效果
慢(秒~小时级)
→ 采样越多越慢
硬件要求 低:普通 GPU 即可流畅运行 高:依赖高性能 CPU/GPU(尤其 OptiX/CUDA)
内存占用 高(尤其复杂场景+高分辨率纹理)
动画渲染 ✅ 非常适合(秒级出帧) ❌ 成本高(需渲染农场)
写实感 ⭐⭐⭐(需大量手动调优) ⭐⭐⭐⭐⭐(开箱即真实)
焦散(Caustics) ❌ 不支持 ✅ 支持(需启用)
体积光/雾 近似(2D 切片模拟) 真实体积散射
景深/运动模糊 后处理模拟(可能失真) 光学精确模拟
透明叠加 Alpha 混合有限(排序问题) 正确处理多次透明穿透

(3)最终实现

使用以下代码片段可以实现渲染时的GPU加速。其中 renderer 是我传入的一个参数,指定需要使用的渲染器类型。

# 设置渲染器
if renderer.upper() == "CYCLES":
	# 1. 设置渲染引擎为 Cycles
	scene.render.engine = 'CYCLES'
	scene.cycles.samples = 64  # 降低采样加快速度
	scene.cycles.preview_samples = 16

	# 2. 确保 cycles 插件启用
	if "cycles" not in prefs.addons:
		bpy.ops.preferences.addon_enable(module='cycles')
	cycles_prefs = prefs.addons["cycles"].preferences

	# 3. 【必须】刷新设备列表
	try:
		cycles_prefs.get_devices()
	except Exception as e:
		print(f"⚠️ get_devices() failed: {e}")
		return False

	# 4. 查看可用设备类型
	available_types = {d.type for d in cycles_prefs.devices}
	print(f"🔍 可用的设备类型: {available_types}")

	# 5. 选择后端(OptiX > CUDA)
	backend_selected = None
	for backend in ['OPTIX', 'CUDA', 'METAL', 'HIP']:
		if backend in available_types:
			if hasattr(cycles_prefs, 'compute_device_type'):
				cycles_prefs.compute_device_type = backend
				backend_selected = backend
				print(f"✅ 使用渲染设备: {backend}")
			break

	if not backend_selected:
		print("❌ 无GPU渲染设备可用.")
		scene.cycles.device = 'CPU'
		return False

	# 6. 启用所有非CPU设备
	gpu_found = False
	for device in cycles_prefs.devices:
		if device.type == "CPU":
			device.use = False
			print(f"🚫 禁用CPU: {device.name}")
		else:
			device.use = True
			gpu_found = True
			print(f"✅ 启用GPU: {device.name} ({device.type})")

	# 7. 设置 GPU 渲染
	scene.cycles.device = 'GPU' if gpu_found else 'CPU'
	print(f"🔧 CYCLES渲染设备设置为: {scene.cycles.device}")

else:
	scene.render.engine = 'BLENDER_EEVEE'

在这里插入图片描述
在这里插入图片描述

重要!超级无敌大坑!!!】
这里必须要提醒一点,如果是带独显的笔记本电脑需要启用GPU加速,请务必去BIOS中设置成独显直连模式。否则系统会因为节能设置,禁止Blender在无窗口模式下的GPU运算。
进坑指南:我最开始在写这段程序之后怎么都无法使用GPU渲染(NVIDIA RTX 4060 laptop),一开始以为是程序写错了,来来回回改,但是相同的程序在Blender脚本编辑器里就能用GPU渲染,PyCharm里就不行。各种查资料,通义千问、DeepSeek、Claude Sonnet 4.5、Grok 4.1等等全都问过一遍了,根据AI说的改了程序还是无法解决,搞了一天多也没搞明白什么问题。最后还得是GPT5-Codex,他直接指出程序没问题,可能是背景渲染降级,然后我在BIOS里调整成独显直连之后就解决了(原先设置的是“自动”)。
在这里插入图片描述

2.3 对目标进行环绕拍摄

(1)功能需求

  • 人工设定摄像机到目标的距离
  • 根据仰角确定摄像机的初始位置
  • 按照旋转角度间隔对目标进行环绕拍摄

(2)最终实现

功能逻辑比较直观简单,最终写成函数形式如下:

def render_orbit_with_annotations(
        target_class,
        scene_class,
        output_dir,
        distance,
        elevation_deg,
        rotation_step_deg=90,
        renderer="EEVEE",
        resolution_x=1920,
        resolution_y=1080
):
    """
    环绕拍摄 + 遮挡检测 + 标注生成
    :param target_class: 目标模型类别
    :param scene_class: 场景模型类别
    :param output_dir: 输出文件夹
    :param distance: 摄像机与目标模型的距离
    :param elevation_deg: 摄像机与目标模型的仰角
    :param rotation_step_deg: 摄像机环绕拍摄时的角度间隔
    :param renderer: 渲染器类型(EEVEE或CYCLES)
    :param resolution_x: 输出图片尺寸(宽)
    :param resolution_y: 输出图片尺寸(高)
    """
    output_dir = str(os.path.join(output_dir, target_class))
    os.makedirs(output_dir, exist_ok=True)

    scene = bpy.context.scene
    prefs = bpy.context.preferences
    scene.render.resolution_x = resolution_x
    scene.render.resolution_y = resolution_y
    scene.render.image_settings.file_format = 'PNG'

    target_obj = bpy.data.objects.get("targetModel")
    if not target_obj:
        raise ValueError("场景中未找到名为 'targetModel' 的对象!")

    # 设置渲染器
    ...

    # 创建相机(复用或新建)
    cam_obj = None
    for obj in bpy.data.objects:
        if obj.type == 'CAMERA':
            cam_obj = obj
            break
    if not cam_obj:
        bpy.ops.object.camera_add(location=(0, -distance, 0))
        cam_obj = bpy.context.active_object
        scene.camera = cam_obj

    json_filename = "metadata.json"
    label_path = os.path.join(output_dir, json_filename)
    if os.path.exists(label_path):
        with open(label_path, 'r', encoding='utf-8') as f:
            annotation_lines = json.load(f)
    else:
        annotation_lines = {}

    # ===== 1. 顶视图(序号 0000)=====
    top_filename = f"{scene_class}_0000.png"
    top_path = os.path.join(output_dir, top_filename)
    should_render_top = not os.path.exists(top_path)

    if should_render_top:
        # 设置顶视图相机
        cam_obj.location = (0, 0, distance)
        direction = Vector((0, 0, 0)) - cam_obj.location
        rot_quat = direction.to_track_quat('-Z', 'Y')
        cam_obj.rotation_euler = rot_quat.to_euler()

        # 检测遮挡
        is_visible, occlusion_ratio, (cx, cy, w, h) = get_visible_info(cam_obj, target_obj, scene)

        # 保存图像
        scene.render.filepath = top_path
        bpy.ops.render.render(write_still=True)
        annotation_lines = annotations_to_json(annotation_lines, top_filename, target_class, scene_class, distance,
                                               elevation_deg, -1, cx, cy, w, h, occlusion_ratio)
        print(f"✅ 顶视图已保存: {top_filename} | 遮挡比例: {occlusion_ratio:.2%}")
    else:
        print(f"⚠️ 顶视图已存在,跳过。")

    # ===== 2. 环绕视图 =====
    angles = []
    current = 0
    while current < 360:
        angles.append(current % 360)
        current += rotation_step_deg
    angles = sorted(set(angles))

    for i, az_deg in enumerate(angles):
        # 获取不冲突的序号(从 0001 开始)
        index = get_next_index(output_dir, scene_class)
        filename = f"{scene_class}_{index:04d}.png"
        filepath = os.path.join(output_dir, filename)

        # 计算相机位置
        elev = math.radians(elevation_deg)
        azim = math.radians(az_deg)
        x = distance * math.cos(elev) * math.sin(azim)
        y = distance * math.cos(elev) * math.cos(azim)
        z = distance * math.sin(elev)
        cam_obj.location = (x, y, z)

        # 对准原点
        direction = Vector((0, 0, 0)) - cam_obj.location
        rot_quat = direction.to_track_quat('-Z', 'Y')
        cam_obj.rotation_euler = rot_quat.to_euler()

        # 检测遮挡与 bbox
        result = get_visible_info(cam_obj, target_obj, scene)
        print(result)
        if not result[0]:
            print(f"⚠️ 视角 {az_deg}°:目标不可见,跳过")
            continue

        is_visible, occlusion_ratio, (cx, cy, w, h) = result
        if occlusion_ratio > 0.5:
            print(f"❌ 视角 {az_deg}° 遮挡比例过高 ({occlusion_ratio:.2%}),跳过保存")
            continue

        # 保存图像
        scene.render.filepath = filepath
        bpy.ops.render.render(write_still=True)
        annotation_lines = annotations_to_json(annotation_lines, filename, target_class, scene_class, distance,
                                               elevation_deg, az_deg, cx, cy, w, h, occlusion_ratio)
        print(f"✅ 已保存: {filename} | 遮挡比例: {occlusion_ratio:.2%}")

    # ===== 3. 保存标注文件 =====
    with open(label_path, 'w') as f:
        json.dump(annotation_lines, f, indent=4)
    print(f"\n📄 标注文件已保存: {label_path}")


def annotations_to_json(annotation_lines, filename, target_class, scene_class, distance, elevation_deg, azimuth_deg,
                        cx, cy, w, h, occlusion_ratio):
    annotation_lines.update({
        filename: [
            {
                "target_class": target_class,
                "scene_class": scene_class,
                "distance": distance,
                "elevation_deg": elevation_deg,
                "azimuth_deg": azimuth_deg,
                "bbox": [cx, cy, w, h],
                "occlusion": occlusion_ratio,
            }
        ],
    })
    return annotation_lines

【输入参数】
model_class:模型类别
output_dir:输出文件夹
distance:摄像机与目标模型的距离
elevation_deg:摄像机与目标模型的仰角
rotation_step_deg:摄像机环绕拍摄时的角度间隔
renderer:渲染器类型(EEVEE或CYCLES)
resolution_x:输出图片尺寸(宽)
resolution_y:输出图片尺寸(高)

2.4 检验环绕拍摄情况

主函数

if __name__ == "__main__":
    # 1. 打开工程
    BLEND_FILE_PATH = r"D:\Projects\RealEarthStudio\Blender模型\合并模型(含光照).blend"
    bpy.ops.wm.open_mainfile(filepath=BLEND_FILE_PATH)

    # 2. 配置参数
    TARGET_MODEL_CLASS = "Bentley"  # 目标模型类名
    SCENE_MODEL_CLASS = "002"  # 环境模型名
    OUTPUT_DIR = r"D:\Projects\RealEarthStudio\Blender照片"  # 输出目录
    DISTANCE = 20  # 相机距离
    ELEVATION = 45  # 环绕仰角
    ROTATION_STEP = 15  # 旋转间隔
    RENDERER = "cycles"  # 渲染器
    RES_X, RES_Y = 4000, 3000  # 分辨率

    # 3. 执行
    render_orbit_with_annotations(
        target_class=TARGET_MODEL_CLASS,
        scene_class=SCENE_MODEL_CLASS,
        output_dir=OUTPUT_DIR,
        distance=DISTANCE,
        elevation_deg=ELEVATION,
        rotation_step_deg=ROTATION_STEP,
        renderer=RENDERER,
        resolution_x=RES_X,
        resolution_y=RES_Y
    )

运行结果

在这里插入图片描述

标注信息:文件名,标注框(cx,cy,w,h),遮挡率

{
    "002_0000.png": [
        {
            "target_class": "Bentley",
            "scene_class": "002",
            "distance": 20,
            "elevation_deg": 45,
            "azimuth_deg": -1,
            "bbox": [
                0.5011711716651917,
                0.5024241656064987,
                0.15481305122375488,
                0.4443792402744293
            ],
            "occlusion": 0.0,
            "renderer": "CYCLES"
        }
    ],
    "002_0001.png": [
        {
            "target_class": "Bentley",
            "scene_class": "002",
            "distance": 20,
            "elevation_deg": 45,
            "azimuth_deg": 0,
            "bbox": [
                0.5004879385232925,
                0.5218518227338791,
                0.15431031584739685,
                0.3283562958240509
            ],
            "occlusion": 0.0,
            "renderer": "CYCLES"
        }
    ],
    ...

打印输出

00:35.812 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0000.png’
✅ 顶视图已保存: 002_0000.png | 遮挡比例: 0.00%
(True, 0.0, (0.5004879385232925, 0.5218518227338791, 0.15431031584739685, 0.3283562958240509))
01:11.281 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0001.png’
✅ 已保存: 002_0001.png | 遮挡比例: 0.00%
(True, 4.181301220939956e-05, (0.5050534904003143, 0.5210772305727005, 0.20145279169082642, 0.32305440306663513))
01:47.265 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0002.png’
✅ 已保存: 002_0002.png | 遮挡比例: 0.00%
(True, 4.181301220939956e-05, (0.5061577409505844, 0.5221357196569443, 0.25180116295814514, 0.30446258187294006))
02:22.546 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0003.png’
✅ 已保存: 002_0003.png | 遮挡比例: 0.00%
(True, 0.0003763171098845961, (0.5057215243577957, 0.5237158536911011, 0.2919991910457611, 0.2762683629989624))
02:58.296 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0004.png’
✅ 已保存: 002_0004.png | 遮挡比例: 0.04%
(True, 0.0004181301220939957, (0.5045098960399628, 0.5227789729833603, 0.3173835873603821, 0.2471572458744049))
03:33.937 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0005.png’
✅ 已保存: 002_0005.png | 遮挡比例: 0.04%
(True, 0.0003345040976751965, (0.5027887225151062, 0.5278141498565674, 0.32932162284851074, 0.21802043914794922))
04:09.515 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0006.png’
✅ 已保存: 002_0006.png | 遮挡比例: 0.03%
(True, 0.0003763171098845961, (0.5012040734291077, 0.5329868346452713, 0.332442045211792, 0.20089182257652283))
04:45.328 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0007.png’
✅ 已保存: 002_0007.png | 遮挡比例: 0.04%
(True, 0.00029269108546579696, (0.5002100169658661, 0.527558907866478, 0.3255322575569153, 0.2347208559513092))
05:21.953 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0008.png’
✅ 已保存: 002_0008.png | 遮挡比例: 0.03%
(True, 0.0002508780732563974, (0.498029962182045, 0.5273079425096512, 0.30754992365837097, 0.2753572165966034))
05:56.734 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0009.png’
✅ 已保存: 002_0009.png | 遮挡比例: 0.03%
(True, 0.00029269108546579696, (0.4955870360136032, 0.5238898247480392, 0.27689483761787415, 0.31252017617225647))
06:31.703 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0010.png’
✅ 已保存: 002_0010.png | 遮挡比例: 0.03%
(True, 0.0, (0.49419108033180237, 0.522959440946579, 0.23159676790237427, 0.3402872681617737))
07:06.484 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0011.png’
✅ 已保存: 002_0011.png | 遮挡比例: 0.00%
(True, 0.0, (0.4952946603298187, 0.5217645615339279, 0.18063420057296753, 0.35422447323799133))
07:41.203 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0012.png’
✅ 已保存: 002_0012.png | 遮挡比例: 0.00%
(True, 0.0, (0.5010006725788116, 0.5212998241186142, 0.15432840585708618, 0.35740819573402405))
08:16.593 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0013.png’
✅ 已保存: 002_0013.png | 遮挡比例: 0.00%
(True, 0.0, (0.504847064614296, 0.5223482549190521, 0.20186111330986023, 0.35006028413772583))
08:51.515 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0014.png’
✅ 已保存: 002_0014.png | 遮挡比例: 0.00%
(True, 0.039136979427997994, (0.5054609328508377, 0.5233142971992493, 0.250334233045578, 0.3310772180557251))
09:26.609 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0015.png’
✅ 已保存: 002_0015.png | 遮挡比例: 3.91%
(True, 0.44380331159056696, (0.5391809642314911, 0.491969957947731, 0.2202187180519104, 0.23227986693382263))
10:02.062 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0016.png’
✅ 已保存: 002_0016.png | 遮挡比例: 44.38%
(False, 0.9136561297875899, (0.626675933599472, 0.4621685594320297, 0.06499260663986206, 0.12416031956672668))
⚠️ 视角 240°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 255°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 270°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 285°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 300°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 315°:目标不可见,跳过
(False, 1.0, None)
⚠️ 视角 330°:目标不可见,跳过
(True, 0.0, (0.49520790576934814, 0.5213552713394165, 0.18065571784973145, 0.326190710067749))
10:41.562 render | Saved: ‘D:\Projects\RealEarthStudio\Blender照片\Bentley\002_0017.png’
✅ 已保存: 002_0017.png | 遮挡比例: 0.00%
📄 标注文件已保存: D:\Projects\RealEarthStudio\Blender照片\Bentley\metadata.json


三、相关附件

  • 文章中使用到的合并目标与场景的Blender工程文件 合并模型(含光照).blend下载链接

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐