WWDC Quick Look 💓 By SwiftGGTeam
Create parametric 3D room scans with RoomPlan

Create parametric 3D room scans with RoomPlan

观看原视频

Highlight

Apple 原型设计团队的 Praveen 和视频工程团队的 Kai 联合介绍了全新的 RoomPlan 框架。RoomPlan 使用 ARKit 和机器学习算法,在配备 LiDAR 的设备上扫描房间,自动检测墙壁、窗户、开口、门以及房间内的重要物体(壁炉、沙发、桌子、柜子等)。


核心内容

做室内设计、房产展示或家具预览时,开发者经常需要一份房间模型。以前,这通常意味着自己处理相机、深度数据、几何重建、物体识别和 3D 导出。每一步都需要专门的机器学习或计算机视觉能力。

RoomPlan 把这个流程收进一个框架里。它运行在配备 LiDAR 的 iPhone 或 iPad 上,用 ARKit 提供的空间理解能力和机器学习模型,生成房间的参数化 3D 模型。(01:01

参数化是这场演讲的关键词。RoomPlan 的输出包含语义化结构,而非一团难以理解的网格。它会识别墙、门、窗、开口,以及沙发、桌子、床、柜子等房间定义对象,并把它们组织成 CapturedRoom 结构。(01:21

Apple 提供了两层入口。(02:57

第一层是 RoomCaptureView。它是开箱即用的扫描界面,负责实时轮廓线、底部 3D 预览和文字引导。适合想快速接入扫描体验的 App。

第二层是 RoomCaptureSession。它暴露实时参数化数据和用户指令。适合需要自定义 AR 界面、把扫描结果叠加到自家可视化系统里的 App。


详细内容

用 RoomCaptureView 快速接入扫描

03:30RoomCaptureView 是一个 UIView 子类。它处理世界空间中的扫描反馈、实时房间模型生成、coaching(引导)和用户提示。

接入扫描只需要保留一个 view 引用和一份 session 配置,然后启动或停止 capture session。(04:36

import UIKit
import RoomPlan

class RoomCaptureViewController: UIViewController {

    var roomCaptureView: RoomCaptureView
    var captureSessionConfig: RoomCaptureSession.Configuration

    private func startSession() {
        roomCaptureView.captureSession.run(configuration: captureSessionConfig)
    }

    private func stopSession() {
        roomCaptureView.captureSession.stop()
    }
}

关键点:

  • import RoomPlan 引入 RoomPlan 框架。
  • RoomCaptureView 承载 Apple 提供的扫描界面。
  • RoomCaptureSession.Configuration 保存本次扫描的配置。
  • run(configuration:) 开始扫描,并把配置传给内部 capture session。
  • stop() 结束扫描,之后框架可以进入处理和结果展示阶段。

这段代码背后的价值在于少写一整套扫描 UI。RoomCaptureView 会在扫描过程中显示检测到的墙、窗、门、开口和物体轮廓,还会用底部 3D 模型显示扫描进度。(04:01

处理扫描结果并导出 USDZ

05:00)如果 App 需要接管结果处理,可以实现 RoomCaptureViewDelegate。演讲展示了两个回调:一个决定是否展示后处理结果,一个拿到最终 CapturedRoom

import UIKit
import RoomPlan

class RoomCaptureViewController: UIViewController {
    func captureView(shouldPresent roomDataForProcessing: CapturedRoomData, error: Error?) -> Bool {
        // Optionally opt out of post processed scan results.
        return false
    }

    func captureView(didPresent processedResult: CapturedRoom, error: Error?) {
        // Handle final, post processed results and optional error.
        // Export processedResults
        try? processedResult.export(to: destinationURL)
    }
}

关键点:

  • CapturedRoomData 是扫描得到、等待后处理的数据。
  • captureView(shouldPresent:error:) 返回 false 时,App 可以选择不展示框架提供的后处理结果界面。
  • CapturedRoom 是后处理后的参数化房间模型。
  • export(to:) 把结果导出到指定 URL,可用于 USDZ 工作流。
  • 代码中的 destinationURL 需要由 App 自己准备,例如写入应用沙盒中的文件路径。

这一步让 RoomPlan 可以进入现有 3D 工具链。演讲后半段提到,导出的 USD 或 USDZ 可以在 Cinema 4D 这类工具中查看层级、尺寸和位置,也可以进入房产、电商、工具和室内设计应用的渲染流程。(12:12

用 RoomCaptureSession 自定义扫描体验

05:40)需要自定义视觉呈现时,可以直接使用 Data API。这个流程分为 scan、process、export 三步。

扫描阶段使用 RoomCaptureSession。它还提供底层 ARSession,让 App 可以在自己的 ARView 中绘制平面和物体边界框。(06:50

import UIKit
import RealityKit
import RoomPlan
import ARKit

class ViewController: UIViewController {
    @IBOutlet weak var arView: ARView!
    var previewVisualizer: Visualizer!

    lazy var captureSession: RoomCaptureSession = {
        let captureSession = RoomCaptureSession()
        arView.session = captureSession.arSession
        return captureSession
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        captureSession.delegate = self
        // set up previewVisualizer
    }
}

关键点:

  • RealityKit 提供 ARView,用于承载自定义 AR 画面。
  • RoomCaptureSession() 创建底层扫描会话。
  • captureSession.arSession 暴露 RoomPlan 使用的 AR session。
  • arView.session = captureSession.arSession 让自定义 ARView 使用同一个 AR session。
  • captureSession.delegate = self 让当前控制器接收实时房间更新和扫描指令。

这里的核心区别是控制权。RoomCaptureView 给你完整界面;RoomCaptureSession 给你数据和事件。自定义 App 可以把 RoomPlan 的实时结果画成自己的品牌界面,也可以把它和已有 AR 内容放在同一个场景里。

接收实时房间模型和扫描指令

07:40RoomCaptureSessionDelegate 提供两类实时反馈:CapturedRoom 更新和 Instruction 指令。

extension ViewController: RoomCaptureSessionDelegate {

    func captureSession(_ session: RoomCaptureSession,
                        didUpdate room: CapturedRoom) {
        previewVisualizer.update(model: room)
    }

    func captureSession(_ session: RoomCaptureSession,
                        didProvide instruction: Instruction) {
        previewVisualizer.provide(instruction)
    }
}

关键点:

  • didUpdate room: 在检测到房间变化时调用。
  • CapturedRoom 是实时的参数化房间数据,可用于更新预览模型。
  • previewVisualizer.update(model:) 代表 App 自己的可视化逻辑。
  • didProvide instruction: 在扫描过程中提供用户引导。
  • Instruction 覆盖距离、扫描速度、光照调整,以及需要关注的纹理区域。

指令很重要。RoomPlan 的扫描质量和人的移动方式、距离、速度、光线有关。框架把这些反馈暴露出来,开发者可以把它们变成 UI 文案、图标或语音提示。(08:33

用 RoomBuilder 生成最终模型

09:04)扫描结束后,RoomBuilder 负责处理 CapturedRoomData,生成最终 CapturedRoom。演讲示例使用了 .beautifyObjects 选项。

import UIKit
import RealityKit
import RoomPlan
import ARKit

class ViewController: UIViewController {

    @IBOutlet weak var arView: ARView!
    var previewVisualizer: Visualizer!

    // set up RoomBuilder
    var roomBuilder = RoomBuilder(options: [.beautifyObjects])
}

关键点:

  • RoomBuilder 是扫描数据后处理入口。
  • options: [.beautifyObjects] 使用 RoomPlan 提供的对象美化选项。
  • RoomBuilder 通常和 RoomCaptureSession 配合使用。
  • previewVisualizer 继续负责展示处理后的最终模型。

RoomCaptureSession 停止或发生错误时,delegate 会收到 CapturedRoomData 和可选错误。随后可以用 Swift concurrency 异步生成最终房间模型。(09:30

extension ViewController: RoomCaptureSessionDelegate {
    func captureSession(_ session: RoomCaptureSession,
                        didEndWith data: CapturedRoomData, error: Error?) {
        if let error = error {
            print("Error: \(error)")
        }

        Task {
            let finalRoom = try! await roomBuilder.capturedRoom(from: data)
            previewVisualizer.update(model: finalRoom)
        }
    }
}

关键点:

  • didEndWith data: 在扫描停止或出错时触发。
  • CapturedRoomData 保存传感器扫描数据,供后处理使用。
  • Task 创建异步上下文。
  • await roomBuilder.capturedRoom(from: data) 异步处理扫描数据。
  • finalRoom 是最终 CapturedRoom,可用于展示、分析或导出。

演讲中说明,这个处理过程通常只需要几秒钟。它使用 Swift async/await,因此不会把后处理写成阻塞 UI 的同步流程。(09:45

理解 CapturedRoom 的结构

10:17)RoomPlan 的输出是参数化结构。顶层 CapturedRoomSurfaceObject 组成。

public struct CapturedRoom: Codable, Sendable {
    public let walls: [Surface]
    public let doors: [Surface]
    public let windows: [Surface]
    public let openings: [Surface]
    public let objects: [Object]

    public func export(to url: URL) throws

    // Surface definitions ...

    // Object definitions ...
}

关键点:

  • CapturedRoom 遵循 Codable,方便编码和持久化。
  • Sendable 表示它可以安全地跨并发边界传递。
  • wallsdoorswindowsopenings 都是 Surface 数组。
  • objects 保存房间内的 3D 对象,演讲中描述为 cuboids(长方体)。
  • export(to:) 把参数化结果导出为 USD 或 USDZ 数据。

Surface 包含墙、门、窗、开口等建筑元素。演讲提到它有曲面相关属性,例如半径、起止角度和四条边;还有尺寸、置信度、3D transform matrix 和唯一标识符。(10:26

Object 表示家具类别,例如 table、bed、sofa。它也带有尺寸、置信度、3D transform matrix 和唯一标识符。(10:49

扫描条件会影响结果

13:09)RoomPlan 最适合单个住宅房间。演讲给出的房间上限是 30 英尺乘 30 英尺,约 9 米乘 9 米。

光线也会影响扫描。Apple 建议至少 50 lux,这相当于家庭客厅夜晚的典型亮度。硬件方面,RoomPlan 支持所有配备 LiDAR 的 iPhone 和 iPad Pro 机型。(13:38

有些环境会带来挑战:全高镜子、玻璃、高天花板和很暗的表面。原因分别来自 LiDAR 预期输出、LiDAR 扫描距离和视频流质量。(14:02

扫描前也可以准备房间。打开窗帘能增加自然光并减少窗户遮挡;关门能减少扫描到房间外区域的概率。高精度场景应该把这些准备步骤放进扫描前提示里。(14:27

最后是电量和散热。Apple 建议避免重复扫描,或超过 5 分钟的单次长扫描。长时间扫描会造成疲劳、耗电和热问题,进而影响 App 体验。(15:16


核心启发

  • 做什么:给室内设计 App 增加自动房间建模入口。 为什么值得做:RoomPlan 会识别墙、窗、门、开口和主要家具,设计工具可以直接基于 CapturedRoom 做墙面改色、面积估算和布局预览。 怎么开始:先用 RoomCaptureView 完成扫描闭环,再在 captureView(didPresent:error:) 中读取 CapturedRoom

  • 做什么:为房产经纪工具生成房间 3D 模型和 USDZ 文件。 为什么值得做:演讲明确提到 real estate apps 可以让经纪人捕获 listing 的 floor plans 和 3D models。 怎么开始:扫描结束后调用 CapturedRoom.export(to:),把结果保存到 App 文件目录,再上传到业务后台或本地预览。

  • 做什么:做一个自定义扫描引导界面。 为什么值得做Instruction 会给出距离、速度、光照和纹理区域反馈,App 可以把它转成更贴合产品语气的引导。 怎么开始:使用 RoomCaptureSessionDelegate,在 didProvide instruction: 中更新自己的提示组件。

  • 做什么:把电商家具预览和真实房间结构结合起来。 为什么值得做:RoomPlan 输出的墙面、开口和家具位置能给商品摆放提供空间上下文。 怎么开始:用 RoomCaptureSession 获取实时 CapturedRoom,在 RealityKit 场景里把商品模型放入扫描得到的房间结构中。

  • 做什么:为高精度扫描设计房间准备流程。 为什么值得做:演讲给出了光照、镜子、玻璃、门和扫描时长等限制。提前引导能减少失败扫描。 怎么开始:在启动 run(configuration:) 前加入检查清单:打开窗帘、关闭房门、提示避开镜面和玻璃、提醒单次扫描控制在 5 分钟内。


关联 Session

评论

GitHub Issues · utterances