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 渲染。SetCameraState和SetLightingState每帧更新相机和灯光。这允许用户旋转视角、移动光源。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 一致,metersPerUnit、upAxis 应该写在 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,从配置文件读取统一的metersPerUnit、diffuseColor等参数。
4. 做一个 AR 场景材质编辑器
- 做什么:在 App 中加载 USDZ 资产,实时编辑
UsdPreviewSurface的diffuseColor、metallic、roughness参数,预览 AR 环境中的效果。 - 为什么值得做:Session 介绍了
UsdPreviewSurface和 IBL 光照元数据。材质参数调好后,在 AR 环境中的表现和在编辑器里可能完全不同。实时预览能减少反复导出测试的迭代次数。 - 怎么开始:用
UsdStage::Open()加载资产,通过 USD API 读写UsdPreviewSurface的属性值,结合 ARKit 的相机画面做合成渲染。
关联 Session
- Understand USD fundamentals — 这场是 10141 的前置,讲 USD 的核心概念:Stage、Layer、Prim、Composition、VariantSet。
- Bring your world into augmented reality — 演示如何用 Object Capture 和 RealityKit 把现实物体带入 AR 场景,和 USD 资产管线直接衔接。
- Discover ARKit 6 — 介绍 ARKit 6 的平台能力,帮助理解 USD 资产最终服务的 AR 场景。
- Create parametric 3D room scans with RoomPlan — RoomPlan 生成结构化 3D 表示,和 USD 的场景层级、资产组织有相近的建模思路。
评论
GitHub Issues · utterances