在 USD 场景中编排媒体资产:usdMedia 域之 SpatialAudio 与 AssetPreviewsAPI 详解
2026/9/17 21:11:21 网站建设 项目流程

在 USD 场景中编排媒体资产:usdMedia 域之 SpatialAudio 与 AssetPreviewsAPI 详解

【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD

UsdMedia是 USD(Universal Scene Description)中负责将音频、缩略图等媒体信息与资产关联的 schema 域,本文基于 OpenUSD 仓库中 docs/user_guides/schemas/usdMedia 下的官方 schema 文档展开。读完本文,你将掌握用SpatialAudio在 USD 场景中实现空间/非空间音频播放、用AssetPreviewsAPI为资产挂接多规格缩略图的方法,理解timecode属性与 layer offset 的相互作用,并能通过源码与测试用例验证其底层行为。

usdMedia 域能做什么

根据官方文档 overview.md 的定义,UsdMedia域为资产提供了两类媒体能力的关联方式:

  • SpatialAudio:允许环境(ambient)音频播放,或从场景中某个具体位置发出的空间音频播放,并可配置多种播放选项;
  • AssetPreviewsAPI:允许为资产设置一个或多个缩略图。这些缩略图可以是资产的预渲染图像,既可用于 DCC 工具的资源浏览器(asset browser),也可用于操作系统级的文件浏览器图标(如 macOS Finder)。

两个 schema 可以同时使用:文档中的assetPreviewsApi.usda示例就同时挂接了AssetPreviewsAPI与两个SpatialAudioprim。下面先分别深入两个 schema,最后给出整合示例。

SpatialAudio:在场景中编排音频播放

Schema 层级与设计意图

从 schema.usda 可以看到,SpatialAudio是一个具体类型(concrete typed)schema,它继承自UsdGeomXformable

class SpatialAudio "SpatialAudio" ( inherits = </Xformable> ... ) { ... }

继承Xformable是有意为之的设计:SpatialAudio 既支持完整的空间音频,也支持非空间的单声道/立体声播放。由于它携带变换信息,一个或多个 SpatialAudio prim 可以放置在命名空间中的任意位置。官方 schema 注释特别建议:真正需要空间定位的音频 prim 应嵌套在声源模型之下,这样音频 prim 只需相对模型做变换,而不必复制模型的动画(详见 spatialAudio.h 的类注释)。

对应生成的 C++ 类UsdMediaSpatialAudio位于 spatialAudio.h,其schemaKindUsdSchemaKind::ConcreteTyped,Python 侧则通过 wrapSpatialAudio.cpp 暴露为UsdMedia.SpatialAudio

典型用法示例

官方文档 SpatialAudio.md 给出了同时包含SpeechAmbient两个音频 prim 的典型场景:

#usda 1.0 ( defaultPrim = "World" endTimeCode = 2400 startTimeCode = 0 timeCodesPerSecond = 24 ) def Xform "World" { def Cube "Cube" { double3 xformOp:translate = (1, 5, -2) uniform token[] xformOpOrder = ["xformOp:translate"] def SpatialAudio "Speech" { uniform token auralMode = "spatial" uniform timecode endTime = 480 uniform asset filePath = @mySpeech.mp3@ uniform token playbackMode = "onceFromStartToEnd" uniform timecode startTime = 240 } } def SpatialAudio "Ambient" { uniform token auralMode = "nonSpatial" uniform asset filePath = @myAmbientTrack.mp3@ uniform token playbackMode = "loopFromStage" } }

从示例中可以观察出以下要点:

  • 空间 vs 非空间SpeechauralMode = "spatial",从场景中 Cube 所在位置发声;AmbientauralMode = "nonSpatial",无论听者位置如何,听起来都一样;
  • 嵌套于 gprim 之下Speech嵌套在Cube内部,当 Cube 被移动或动画化时,音频源会跟随其位置;
  • 播放时长换算Speech播放 10 秒,计算方式为(endTime - startTime) / timeCodesPerSecond = (480 - 240) / 24 = 10
  • 音频文件内的时间窗mySpeech.mp3从文件第startTime / timeCodesPerSecond = 240 / 24 = 10秒处开始播放,到第480 / 24 = 20秒处结束;
  • 是否循环SpeechplaybackMode = "onceFromStartToEnd"不循环;AmbientplaybackMode = "loopFromStage"循环播放;
  • 覆盖整个场景Ambient覆盖场景的全部 100 秒(stage 的startTimeCode = 0endTimeCode = 2400,即 2400/24 = 100 秒)。

属性详解

SpatialAudio的 7 个自有属性定义于 schema.usda,以下参数说明均来自官方文档 SpatialAudio.md 的属性章节。

filePath
  • USD 类型asset
  • 默认值@@(空资产路径)
  • 含义:音频文件路径。预期支持 m4a、mp3、wav 格式。

需要说明的是,USD 本身对音频格式的限制并不比图片格式更严格(参见 schema.usda 中filePath的 doc 注释);但usdz 有更严格的约束——基于 DMA(Digital Media Asset)规范及浏览器、消费级设备的格式支持,usdz 允许的音频文件类型按优先级依次为M4A、MP3、WAV。因此,若要打包进 usdz 资产,应优先选用 m4a。

auralMode
  • USD 类型token
  • 默认值spatial
  • 允许值spatialnonSpatial
  • 含义:决定音频如何播放:
    • spatial:如果设备支持空间音频,则在 3D 空间中播放,否则回退为单声道(mono)。适用于角色等需要声音始终从其位置发出的对象;
    • nonSpatial:不考虑 SpatialAudio prim 的位置。如果媒体本身包含立体声或多声道内容,是否考虑听者位置由应用程序决定。官方期望nonSpatial用于环境音与背景音乐音轨。

值得注意,spatialnonSpatial的枚举值定义在 tokens.h,通过UsdMediaTokens->spatial等 token 访问;auralMode 属性的 C++ 访问接口为GetAuralModeAttr()/CreateAuralModeAttr()(见 spatialAudio.h)。

playbackMode
  • USD 类型token
  • 默认值onceFromStart
  • 允许值onceFromStartonceFromStartToEndloopFromStartloopFromStartToEndloopFromStage
  • 含义:决定音频播放的整体规则,用于指定何时开始、何时停止以及是否循环。

各取值与“是否循环 / 开始时间 / 结束时间”的关系见下表(原文档表格):

ValueAudio Loops?StartTimeEndTime
onceFromStartprim 的startTime音频文件末尾
onceFromStartToEndprim 的startTimeprim 的endTime,或音频文件末尾(取先到者)
loopFromStartprim 的startTimestage 的endTimeCode
loopFromStartToEndprim 的startTimeprim 的endTime
loopFromStagestage 的startTimeCodestage 的endTimeCode
startTime
  • USD 类型timecode
  • 默认值0
  • 含义:以 timeCode 为单位,从音频文件开头算起的开始播放偏移。

换算示例(来自文档):一个 10 秒的音频文件,stage 的timeCodesPerSecond为 24,startTime设为 24.0,则音频从文件第 1 秒处开始播放(24 / 24 = 1)。

startTime使用timecode而非普通 double 类型,是为了让 stage 在值解析时能正确应用 layer offset(详见下文“Layer Offsets”一节)。当playbackMode = "loopFromStage"时该值被忽略,因为此模式下音频总是从 stage 的startTimeCode开始(见 schema.usda)。

endTime
  • USD 类型timecode
  • 默认值0
  • 含义:以 timeCode 为单位,从音频文件开头算起的结束播放偏移。仅当引用的音频片段比期望播放时长更长时生效。

换算示例(来自文档):一个 10 秒的音频文件,stage 的timeCodesPerSecond为 24,endTime设为 48.0,则音频在文件第 2 秒处停止播放(48 / 24 = 2)。

endTime仅在playbackModeonceFromStartToEndloopFromStartToEnd时生效,否则使用 stage 的endTimeCode代替。一个值得注意的边界行为:如果endTime小于startTime,预期音频将从endTime播放到startTime(即反向区间),详见 schema.usda。

mediaOffset
  • USD 类型double
  • 默认值0.0
  • 含义:以为单位,当 stage 播放到达该 prim 音频应开始的时刻时,从被引用音频文件开头算起的播放偏移量。

换算示例(来自文档):一个 10 秒的音频文件,mediaOffset为 3.0,则前 3 秒不播放,之后从音频文件开头开始播放。对于循环音频,该偏移仅作用于第一轮播放,第二轮及之后都从音频文件开头开始(见 schema.usda)。

startTime/endTime不同,mediaOffset的单位是秒而非 timeCode,因此在 layer offset 作用于 timecode 属性时,mediaOffset不受影响——这一点在测试用例中有直接体现(见下文)。

gain
  • USD 类型double
  • 默认值1.0
  • 含义:音频信号的倍率。值为 0 时表示静音;负值会被钳制为 0(见 schema.usda)。

继承自 Xformable/Imageable 的属性

由于SpatialAudio继承自UsdGeomXformable(进而继承UsdGeomImageable),它还包含以下继承属性(见 SpatialAudio.md 的属性章节):

属性USD 类型默认值说明
xformOpOrdertoken[]变换操作顺序(Xformable)
proxyPrimrel代理 prim(关系属性)
purposetokendefaultprim 的用途(Imageable)
visibilitytokeninherited可见性(Imageable)

SpatialAudio 与 Layer Offsets

这是SpatialAudio使用中最容易忽略也最重要的一个行为(原文档专门设有 "SpatialAudio and Layer Offsets" 一节):

  • 当某 layer 带有layer offset时,该偏移信息会在**值解析(value resolution)**阶段被应用到timecode类型的属性值上;
  • 由于startTimeendTime都是timecode属性,这意味着SpatialAudio 的播放时间会被 layer offset 调整,从而可以与同一 layer 中时间采样(time sampled)的动画保持同步
  • 重要限制:如果 layer offset 包含时间缩放(time scale),USD 不会对实际音频媒体做任何播放拉伸(playback dilation)。schema 的 doc 注释解释了原因(schema.usda):由于startTimeendTime可以在具有不同时间缩放的不同 layer 中独立编写,通常无法定义用于计算拉伸的“原始时间框架”;即便能计算出组合后的拉伸,也不可能在将 stage 或 layer stack 拍平(flatten)为单个 layer 时保留组合后的音频拉伸效果。

该行为在测试用例 testUsdMediaSpatialAudio.py 中得到了直接验证:测试对RefAudio建立引用并施加layerOffset = Sdf.LayerOffset(scale=2.0, offset=10.0),随后在引用层分别设置startTime = 10endTime = 200。组合解析后,外层 prim 的GetStartTimeAttr().Get()返回Gf.TimeCode(30)(10 × 2 + 10),GetEndTimeAttr().Get()返回Gf.TimeCode(410)(200 × 2 + 10)——偏移与缩放都被应用;而mediaOffset = 5.0保持不变,印证了其以秒为单位、不参与 timecode 偏移的特性。

AssetPreviewsAPI:为资产挂接缩略图预览

应用式 Schema 与 assetInfo 编码

SpatialAudio不同,AssetPreviewsAPI是一个应用式(applied)API schema,类型为singleApply(见 schema.usda 中的customData.apiSchemaType = "singleApply"),继承自UsdAPISchemaBase。其核心特点:

  • 它可以被应用到 stage 上任意数量的 prim;
  • 要访问 stage 的“默认”预览,需查看 stage 的defaultPrim
  • 该 schema不定义任何属性或元数据回退值——预览信息完全编码在 prim 的assetInfo元数据中;
  • 由于只涉及资产路径,直接消费返回数据的客户端需要通过会话的ArAssetResolver获取ArAsset(见 assetPreviewsAPI.h)。

assetInfo中的字典键由 schema token 定义(schema.usda):

Token含义
previewspreviewsassetInfo 中预览子字典的键
thumbnailsthumbnailspreviews 字典中缩略图子字典的键
defaultImagedefaultImage缩略图字典中默认图像的键
previewThumbnailspreviews:thumbnails缩略图字典的完整路径键
previewThumbnailsDefaultpreviews:thumbnails:default“默认”缩略图的完整路径键

缩略图字典结构与多规格支持

官方文档 AssetPreviewsAPI.md 给出的示例展示了一个资产可以同时关联多个缩略图,并指定其中一个为默认:

#usda 1.0 ( defaultPrim = "World" metersPerUnit = 0.01 upAxis = "Y" ) def Xform "World" ( prepend apiSchemas = ["AssetPreviewsAPI"] assetInfo = { dictionary previews = { dictionary thumbnails = { dictionary default = { asset defaultImage = @defaultThumbnail.jpg@ } dictionary highResolution = { asset defaultImage = @highResolution.jpg@ } dictionary wireFrame = { asset defaultImage = @wireFrame.jpg@ } } } } ) { def Cube "Cube" { } }

要点:

  • AssetPreviewsAPI必须被应用(通过prepend apiSchemas = ["AssetPreviewsAPI"]),否则任何查询 API 都不会成功;
  • 缩略图按“命名子字典 + 统一的defaultImage键”组织:上例中有defaulthighResolutionwireFrame三套缩略图,其中default是默认缩略图;
  • 这样应用就可以按需支持不同质量或风格的缩略图;
  • 缩略图没有规定尺寸,但官方 schema 注释提醒:要注意缩略图的引入不应显著增大资产整体体积(例如打包进 USDZ 时),见 assetPreviewsAPI.h。

编程接口:Thumbnails 与 Get/Set/Clear

UsdMediaAssetPreviewsAPI类(生成头文件 assetPreviewsAPI.h)在生成代码之外还提供了自定义 API:

  • Thumbnails值类型:作为序列化/反序列化assetInfo["previews:thumbnails"]字典的辅助结构,目前持有defaultImageSdfAssetPath类型)一个成员;
  • GetDefaultThumbnails(Thumbnails*):读取默认缩略图数据,成功返回true。实现通过prim.GetAssetInfoByKey(UsdMediaTokens->previewThumbnailsDefault)取字典,再以defaultImagetoken 取图像路径(见 assetPreviewsAPI.cpp);
  • SetDefaultThumbnails(const Thumbnails&):将缩略图数据写回assetInfo["previews:thumbnails:default"],内部使用prim.SetAssetInfoByKey(...)(assetPreviewsAPI.cpp);
  • ClearDefaultThumbnails():在当前UsdEditTarget中删除默认缩略图的完整条目,内部调用prim.ClearAssetInfoByKey(...)(assetPreviewsAPI.cpp);
  • 静态工厂GetAssetDefaultPreviews(layerPath / layer):以layerPath(或SdfLayerHandle)构造一个最小 stage,并返回针对该 stage 的defaultPrim的预览 schema 对象;通过字符串路径调用等价于GetAssetDefaultPreviews(SdfLayer::FindOrOpen(layerPath))

上述 API 在 Python 侧通过 wrapAssetPreviewsAPI.cpp 暴露为UsdMedia.AssetPreviewsAPI

测试用例验证的行为边界

测试 testUsdMediaAssetPreviewsAPI.py 验证了几个关键行为,值得在使用时注意:

  1. 未应用 schema 时一切查询失败:仅构造UsdMedia.AssetPreviewsAPI(xform)并调用SetDefaultThumbnails后,GetDefaultThumbnails()仍返回False——因为 schema 尚未应用;
  2. Apply之后才生效:调用UsdMedia.AssetPreviewsAPI.Apply(xform.GetPrim())后,GetDefaultThumbnails()才能取回写入的defaultImage
  3. GetAssetDefaultPreviews依赖defaultPrim:stage 未设置defaultPrim时,GetAssetDefaultPreviews(layer)返回失败;设置stage.SetDefaultPrim(...)后才成功;
  4. ClearDefaultThumbnails后数据为空:清除后GetDefaultThumbnails()返回None

整合示例:同时使用两个 Schema

官方文档 overview.md 提供的assetPreviewsApi.usda将两个 schema 整合在同一个文件中——既为资产提供缩略图预览,又在场景中编排两段音频:

#usda 1.0 ( defaultPrim = "World" endTimeCode = 2400 startTimeCode = 0 timeCodesPerSecond = 24 ) def Xform "World"( prepend apiSchemas = ["AssetPreviewsAPI"] assetInfo = { dictionary previews = { dictionary thumbnails = { dictionary default = { asset defaultImage = @defaultThumbnail.jpg@ } } } } ) { def Cube "Cube" { double3 xformOp:translate = (1, 5, -2) uniform token[] xformOpOrder = ["xformOp:translate"] def SpatialAudio "Speech" { uniform token auralMode = "spatial" uniform timecode endTime = 480 uniform asset filePath = @mySpeech.mp3@ uniform token playbackMode = "onceFromStartToEnd" uniform timecode startTime = 240 } } def SpatialAudio "Ambient" { uniform token auralMode = "nonSpatial" uniform asset filePath = @myAmbientTrack.mp3@ uniform token playbackMode = "loopFromStage" } }

从该示例中可以观察到:

  • 空间与非空间音频的差异Speech(空间,跟随 Cube 位置)与Ambient(非空间,环境铺底)并存;
  • 缩略图的关联方式assetInfo.previews.thumbnails.default.defaultImage为资产提供了默认预览图,可被 DCC 或系统资源浏览器直接消费;
  • 嵌套的价值Speech嵌套在Cubegprim 之下,Cube 被移动或动画时,声源自动跟随。

文档还给出了缩略图信息的实际呈现效果:macOS 的 Finder 文件浏览器会把defaultThumbnail.jpg直接用作assetPreviewsApi.usda文件的图标(见上文配图,原图位于 docs/user_guides/schemas/usdMedia/usdMediaExample.jpg),这正是 AssetPreviewsAPI 在系统级资源浏览场景中的直观体现。

要点速查

  1. 两个 schema 分工明确SpatialAudio(concrete typed,继承UsdGeomXformable)负责场景音频播放;AssetPreviewsAPI(single-apply 应用式 API schema)负责把缩略图写进 prim 的assetInfo元数据,二者可同时应用于同一资产。
  2. SpatialAudio 属性速记filePath(m4a/mp3/wav,usdz 按 M4A、MP3、WAV 优先级)、auralMode(spatial/nonSpatial)、playbackMode(5 种取值,决定循环与起止)、startTime/endTime(timecode,受 layer offset 影响)、mediaOffset(秒,循环时仅首轮生效)、gain(倍率,负值钳为 0)。
  3. timecode 的双刃剑startTime/endTime使用timecode类型使音频与动画在 layer offset 下保持同步,但 layer offset 的时间缩放不会对音频媒体做拉伸。
  4. AssetPreviewsAPI 必须 Apply:无论 C++ 还是 Python,未应用 schema 的 prim 上所有预览查询都会失败;默认预览总是查 stage 的defaultPrim
  5. 深入阅读路径:schema 权威定义见 pxr/usd/usdMedia/schema.usda,生成 API 见 spatialAudio.h 与 assetPreviewsAPI.h,行为验证见 testUsdMediaSpatialAudio.py 与 testUsdMediaAssetPreviewsAPI.py。

【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询