WWDC Quick Look 💓 By SwiftGGTeam
Explore USD tools and rendering

Explore USD tools and rendering

观看原视频

Highlight

这场 Session 是 “Understand USD fundamentals”(10129)的姊妹篇,聚焦在 Apple 平台上实际使用 USD 的工具和渲染管线。如果你已经理解了 USD 的基本概念(Prim、Layer、Composition),这场 Session 告诉你用什么工具来创建、编辑和渲染 USD 内容。


核心内容

10129 解释了 USD 是什么——Stage、Layer、Prim、Composition 这些概念。这场 10141 接着讲怎么做:用什么工具把模型转成 USDZ、怎么验证资产合规性、怎么把 Hydra 渲染引擎集成到自己的 App 里。

Session 分两条线。第一条是命令行工具:usdzconvert 负责格式转换,usdzcheck 负责资产验证。第二条是渲染:Hydra 是 USD 的渲染抽象层,Storm 是 Hydra 的一个光栅化实现,Apple 演示了如何用 Metal 驱动 Storm,在自己的 App 里渲染 USD 场景。


详细内容

usdzconvert:把 3D 模型转成 USDZ

03:00)大部分 3D 资产不会一开始就是 USD 格式。建模工具导出的是 OBJ、FBX、GLTF 等格式。usdzconvert 这个命令行工具负责把它们转成 USDZ。

% python usdzconvert --help
usdzconvert 0.66
usage: usdzconvert inputFile [outputFile]
                   [-h] [-version] [-f file] [-v]
                   [-path path[+path2[...]]]
                   [-url url]
                   [-copyright copyright]
                   [-copytextures]
                   [-metersPerUnit value]
                   // ...
                   [-diffuseColor           r,g,b]
                   [-diffuseColor           <file> fr,fg,fb]
                   [-normal                 x,y,z]
                   [-normal                 <file> fx,fy,fz]
                   // ...

关键参数:

  • -metersPerUnit:设置场景尺度单位。这和 10129 中提到的 Stage metadata 直接对应。如果源模型是厘米而目标场景是米,这里设错会导致模型大小完全不对。
  • -diffuseColor-normal:可以在转换时覆盖材质属性。比如统一给一批模型设基础颜色,或指定法线贴图。
  • -copytextures:把纹理文件一起打包进 USDZ。USDZ 本质是未压缩 zip,纹理放在包里,加载时不需要额外路径。

工具随 Xcode 命令行工具包安装。转换时会尽量保留材质、纹理和动画,但不是所有源格式的特性都能无损映射。

usdzcheck:验证 USDZ 资产合规性

Session 强调了验证环节:每次修改 USDZ 后,都应该跑一遍 usdzcheck。这个工具检查的内容包括:

  • 文件结构是否正确
  • 纹理格式是否受支持
  • polycount 是否在合理范围
  • 层级结构是否符合 Apple 平台要求

通过验证的 USDZ 文件可以在所有 Apple 平台(iOS、macOS、watchOS)上正确加载和渲染。Session 建议把 usdzcheck 集成到 CI 管线中——资产提交前自动跑验证,避免不合规资产进入 App。

灯光元数据:在 USDA 中声明光照偏好

09:00)USD 资产可以在 metadata 中声明光照偏好。Session 展示了通过 customLayerData 指定 IBL(Image-Based Lighting)版本:

// asset.usda
#usda 1.0
(
    customLayerData = {
        dictionary Apple = {
             int preferredIblVersion = 2
        }
    }
)

preferredIblVersion = 2 告诉渲染器使用第二版 IBL 方案。这类 metadata 写在 Stage 层级,影响整个场景的默认光照。对于 AR 场景来说,正确设置光照元数据可以避免模型放进真实环境后”发飘”——光照和周围环境不匹配是最容易暴露 3D 模型的问题之一。

材质系统:UsdPreviewSurface

Apple 的 USD 实现使用 UsdPreviewSurface 作为标准材质。这是 USD 规范里定义的基础 PBR 材质,支持以下核心属性:

  • diffuseColor:漫反射颜色
  • metallic:金属度
  • roughness:粗糙度
  • normal:法线贴图

RealityKit 在渲染时自动把 UsdPreviewSurface 转换成自己的材质系统。这意味着资产作者不需要了解 RealityKit 的材质模型——按 USD 标准写好 UsdPreviewSurface,RealityKit 就能正确渲染。

构建 USD + Hydra

17:50)要在自己的 App 中渲染 USD 场景,需要构建 USD 和 Hydra。Session 给出了构建步骤:

// Apple Silicon Mac 上需要 Rosetta
% arch -x86_64 /bin/zsh

// 下载源代码
% git clone https://github.com/PixarAnimationStudios/USD.git

// 构建 USD + Hydra(使用 Xcode generator)
% python3 USD/build_scripts/build_usd.py --generator Xcode --no-python USDInstall

关键参数:

  • --generator Xcode:生成 Xcode 项目而非 Makefile,方便在 Xcode 中调试和集成。
  • --no-python:跳过 Python 绑定,减少构建产物。如果你的 App 不需要 Python 脚本能力,加上这个参数。
  • USDInstall:指定安装目录。

Hydra 是 USD 的渲染抽象层。它定义了渲染器需要实现的接口,但不绑定具体渲染技术。Storm 是 Hydra 的一个光栅化实现,Apple 演示了如何用 Metal 作为后端驱动 Storm。

加载 USD Stage

18:54)Session 演示了在 macOS App 中通过文件选择器加载 USD 文件:

// AAPLViewController.mm

- (void)viewDidAppear
{
    NSOpenPanel* panel = [NSOpenPanel openPanel];
    panel.allowedContentTypes = @[UTTypeUSD, UTTypeUSDZ];

    [panel beginWithCompletionHandler:^(NSModalResponse result) {
        if (result == NSModalResponseOK)
        {
            NSURL* url = panel.URLs[0];
            [self->_renderer setupScene:[url path]];
        }
    }];
}

// AAPLRenderer.mm

- (bool)loadStage:(NSString*)filePath
{
    _stage = UsdStage::Open([filePath UTF8String]);
    // ...
}

UsdStage::Open() 打开 USD 文件,返回一个 Stage 对象。这个 Stage 就是 10129 里讲的那个 Stage——它包含了所有 Layer 的 Composition 结果。之后渲染、查询、编辑都基于这个 Stage。

设置场景相机

19:30)加载 Stage 后,需要设置观察相机:

// AAPLRenderer.mm

- (void)setupCamera
{
    _viewCamera = [[AAPLCamera alloc] initWithRenderer:self];

    [self calculateWorldCenterAndSize];

    [_viewCamera setDistance:_worldSize];
    [_viewCamera setFocus:_worldCenter];
}

calculateWorldCenterAndSize 遍历 Stage 的包围盒,计算场景中心和尺寸。然后用这个信息设置相机距离和焦点,确保整个场景都在视野内。这步做完后,用户打开任何 USD 文件都能自动看到合适的视角。

设置场景灯光

19:54)Session 演示了一种简单的灯光策略:把相机位置当作光源位置:

// AAPLRenderer.mm

GlfSimpleLight computeCameraLight(const GfMatrix4d& cameraTransform)
{
    GlfSimpleLight light;
    light.SetPosition(GfVec4f(cameraPosition[0], cameraPosition[1], cameraPosition[2], 1));

    return light;
}

这是一个简化的做法——灯光始终从观察者方向打过来。对于预览和调试场景足够了。生产环境通常需要更复杂的光照设置,比如配合前面提到的 IBL 元数据。

初始化 Hydra 引擎并渲染

20:17)核心渲染流程分两步:初始化 UsdImagingGLEngine,然后在每帧调用绘制:

// AAPLRenderer.mm

- (void)initializeEngine
{
    _engine.reset(new UsdImagingGLEngine(_stage->GetPseudoRoot().GetPath(),
                                         excludedPaths,
                                         SdfPathVector(),
                                         SdfPath::AbsoluteRootPath(),
                                         driver));
}

// AAPLRenderer.mm

- (HgiTextureHandle)drawWithHydraAt:(double)timeCode
                           viewSize:(CGSize)viewSize
{
      _engine->SetCameraState(modelViewMatrix, projMatrix);
      _engine->SetLightingState(lights, _material, _sceneAmbient);

      UsdImagingGLRenderParams params;
      params.clearColor = GfVec4f(0.0f, 0.0f, 0.0f, 0.0f);
      params.frame = timeCode;

      // ...
}

关键点:

  • UsdImagingGLEngine 是 Hydra 和 OpenGL/Metal 之间的桥接。它把 USD 场景数据转换成 Hydra 能消费的格式,然后交给 Storm 渲染。
  • SetCameraStateSetLightingState 每帧更新相机和灯光。这允许用户旋转视角、移动光源。
  • UsdImagingGLRenderParams 控制渲染参数。clearColor 设背景色,frame 设时间码(用于动画)。
  • Apple 的实现中,HgiTextureHandle 返回的纹理可以直接交给 Metal 管线做后续合成——比如把 3D 渲染结果叠加到 AR 相机画面上。

Quick Look 和 AR Quick Look

Safari、邮件、信息等系统 App 支持 USDZ 的 Quick Look 预览。用户点击 USDZ 链接,系统弹出 3D 预览窗口,可以旋转查看模型,或切换到 AR 模式把模型放到现实环境中。

开发者可以通过 QLPreviewController 在自己的 App 中集成同样的能力。如果你的 App 只是需要展示 USDZ 模型而非自定义渲染,直接用 Quick Look 比集成 Hydra 简单得多。


最佳实践

1. 把 usdzcheck 放进 CI 管线

每次修改 USDZ 资产后都跑 usdzcheck。它能提前发现格式不合规和性能问题——这些在开发机上可能不显现,但在低端设备上会导致加载失败或掉帧。把它集成到 CI 里,资产提交前自动验证。

2. 纹理使用 Power-of-Two 分辨率

纹理尺寸设为 256、512、1024、2048。RealityKit 的纹理管线对 POT 分辨率有专门优化。非 POT 纹理会触发自动缩放,可能降低质量或增加加载时间。

3. 模型层级匹配交互需求

如果用户需要单独操控模型的某个部分(比如旋转车轮),确保那个部分在 USD 里是一个独立的 Prim,而不是和其他几何体合并在同一个 mesh 里。RealityKit 的 entity 层级直接反映 USD 的 Prim 层级——层级设计决定了交互粒度。

4. 在资产中声明全局 metadata

和 10129 一致,metersPerUnitupAxis 应该写在 Stage metadata 中。转换资产时用 usdzconvert 的对应参数设置,而不是在 App 代码里临时修正。metadata 写在资产里,所有消费方都能拿到一致的信息。


核心启发

1. 搭建 USD 资产 CI 验证管线

  • 做什么:在 CI 中集成 usdzcheck,每次资产提交自动验证格式合规性和性能指标。
  • 为什么值得做:Session 强调资产验证是”容易被跳过但后果严重”的步骤。不合规资产在开发机上正常,到低端设备上加载失败或掉帧。自动化验证消除人工检查。
  • 怎么开始:写一个简单的 shell 脚本调用 usdzcheck,在 Git pre-commit hook 或 CI pipeline 中执行。输出检查报告,不合规则阻止合并。

2. 做一个 USD 场景预览器

  • 做什么:用 Hydra + Metal 构建一个轻量 USD/USDZ 文件预览 App,支持旋转视角、切换光照模式。
  • 为什么值得做:Session 给出了完整的集成代码:构建 USD + Hydra、加载 Stage、设置相机和灯光、渲染到 Metal 纹理。把这些串起来就是一个可用的预览器。
  • 怎么开始:按 Session 的步骤构建 USD + Hydra,实现 UsdImagingGLEngine 初始化,用 QLPreviewController 或自定义 Metal 视图显示渲染结果。

3. 做一个批量格式转换工具

  • 做什么:基于 usdzconvert 封装批量转换脚本,支持 OBJ/FBX/GLTF 到 USDZ 的目录级转换,自动设置 metersPerUnit 和材质参数。
  • 为什么值得做:Session 演示了 usdzconvert 的丰富参数。实际项目中,模型师导出大量模型后需要统一转换和参数设置,手动逐个处理效率太低。
  • 怎么开始:写一个 Python/Shell 脚本遍历目录,对每个模型文件调用 usdzconvert,从配置文件读取统一的 metersPerUnitdiffuseColor 等参数。

4. 做一个 AR 场景材质编辑器

  • 做什么:在 App 中加载 USDZ 资产,实时编辑 UsdPreviewSurfacediffuseColormetallicroughness 参数,预览 AR 环境中的效果。
  • 为什么值得做:Session 介绍了 UsdPreviewSurface 和 IBL 光照元数据。材质参数调好后,在 AR 环境中的表现和在编辑器里可能完全不同。实时预览能减少反复导出测试的迭代次数。
  • 怎么开始:用 UsdStage::Open() 加载资产,通过 USD API 读写 UsdPreviewSurface 的属性值,结合 ARKit 的相机画面做合成渲染。

关联 Session

评论

GitHub Issues · utterances