USD术语与概念
USD引入了相当多的术语和概念,其中一些不可避免地在其他上下文中已经具有不同的含义,因此在您完全掌握之前,要理解我们所有的文档和代码可能会相当令人生畏。本文档试图定义和解释在USD文档的其他部分中提到的所有主要概念和行为。
激活(Active) / 非激活(Inactive)
激活是启动物(prims)的元数据(metadatum)/行为,它模拟了一个阶段的“非破坏性和可逆的启动物删除”。默认情况下,prim是活动的,这意味着它们和它们的活动子节点将由阶段遍历组合和访问。然而,通过调用UsdPrim::SetActive(false)使prim处于非活动状态,我们可以防止默认遍历访问prim本身,并且我们还可以防止在舞台上组合prim的后代prim,这使得停用成为修剪不需要的场景描述以进行可伸缩性和复杂性管理的有用工具。
在下面的示例中,prim at路径/Parent已被停用,因此/Parent/Child1和其他子路径将不会被组合。但是,/Parent的活动元数据可以在更强的层中被重写为true,这将导致/Parent/Child1等组合到舞台上。
def Xform "Parent" (
active = false
)
{
def Mesh "Child1"
{
}
# other siblings of "Child1" ...
}
API 模式(API Schema)
API模式是作为编写和提取一组相关数据的接口或API的prim schema,也可能有助于prim的定义。就USD对象模型而言,API模式是从 UsdAPISchemaBase 派生出来的,而不是从它的子类 UsdTyped 派生出来的。这意味着UsdPrim::IsA<UsdModelAPI>()
将永远不会返回true。与类型化的“是一个”模式相比,将API模式视为“有一个”模式。有三种类型的API模式:非应用、单应用和多应用。
-
非应用 API Schemas
从某种意义上说,非应用的API模式是“最弱的”API模式,因为它们提供的只是用于设置和获取一些相关属性和/或元数据的API,而不会以任何方式为prim的类型或定义做出贡献。UsdModelAPI是一个非应用模式的例子,其目的是与一些与模型和资产相关的原始元数据进行交互;没有办法判断是否在prim上存在UsdModelAPI,而是简单地使用它的方法来询问特定方面。
Usd.ModelAPI(prim).SetKind(Kind.Tokens.subcomponent)
还可以创建一个非应用的模式,以便与存在于一些其他类型模式上的一些相关(出于您的目的)属性进行交互,这可能允许更简洁的编码模式。 -
单应用、多应用 API Schemas
应用的模式被记录到prim的aprequas元数据中,这允许它们对prim的定义做出贡献,添加内置属性,并提交到状态查询,使用UsdPrim::HasAPI<UsdShadeMaterialBindingAPI>()
。最常见的API模式类型是单应用,它应该在使用模式的编写API之前应用。也就是说,当我们创建类型化模式时,通常在使用模式来创建数据之前,在定义prim时赋值类型,如:
mesh = UsdGeom.Mesh.Define(stage, path)
mesh.CreateSubdivisionSchemeAttr().Set(UsdGeom.Tokens.bilinear)
对于已应用的API模式,我们在使用模式创建数据之前,将类型应用于现有的prim,如:
bindingAPI = UsdShade.MaterialBindingAPI.Apply(prim)
bindingAPI.Bind(materialPrim)
多重应用模式可以不止一次地应用于一个对象,需要一个“实例名”来区分一个应用程序和另一个应用程序——实例名将构成命名空间的一部分,模式的属性将在命名空间中生成。UsdCollectionAPI是一个多应用模式的例子,UsdShadeMaterialBindingAPI使用它将材料分配给prim集合,允许多个集合位于单个prim上。
当您有一组相关属性、元数据和可能关联的行为需要应用于多种不同类型的prim时,请选择创建应用的API Schema。例如,如果您的管道有一组三个属性,这些属性被写入到每个gprim上,并且您希望有一个健壮的模式来编写和提取这些属性,那么您可以为它们创建一个应用的API模式。为什么不继承类型化的UsdGeomGprim模式并在那里添加属性呢?因为这样您就需要重新定义从Gprim派生的所有模式类,从而阻止您利用对usgeomgprim派生类的内置DCC支持。API模式提供“混合”数据组织。
API模式可以使用USD模式生成工具生成,并使用自定义方法进行扩展,就像类型化模式一样。我们甚至对没有内置属性的未应用API模式也使用模式生成,以确保一致性。
装配 (Assembly)
在USD中,装配是一种模型。程序集是组模型,即将其他模型聚合成有意义的集合的模型。程序集可能主要由对其他模型的引用组成,并且它们本身是发布的资产。
def Xform "Forest_set" (
kind = "assembly"
)
{
# Possibly deep namespace hierarchy of prims, with references to other assets
}
一个“装配”资产,在场景描述中声明
参见:模型层次结构
资产(Asset)
资产是内容生成管道中相当常见的组织概念。在美元中最通用的术语中,资产是可以用字符串标识符识别和定位的东西(通过资产解析)。为了方便诸如资产依赖分析之类的操作,USD定义了一个专门的字符串类型asset,这样所有引用资产的元数据和属性都可以被快速和健壮地识别出来。在内容管道中,资产有时是单个文件(例如UV纹理),或由单个文件锚定的文件集合,该文件依次引用其他文件。资产的一个非定义性但重要的品质是它们通常是发布和版本控制的。为了帮助资产管理和分析组合场景,USD提供了一个AssetInfo模式。.usda格式文件对资产值字符串使用一种特殊的语法,使它们易于与普通字符串区分,使用“@”符号代替引号来分隔它们的值,或者如果资产路径本身包含“@”字符,则使用“@@”作为分隔符。如果资源路径必须包含字符串“@@@”,那么它应该被单独转义为“@@@”。有关这两种形式的示例,请参阅AssetInfo。
资产信息(AssetInfo)
AssetInfo是一个元数据字典,可以应用于任何UsdPrim或UsdProperty,以传递与资产识别和管理相关的信息。通常情况下,usstage通过组合弧线吸收许多资产。然而,当与舞台上的道具和属性进行交互时,弧线的存在和位置以及它们所瞄准的资产的身份被设计为隐藏在执行细节中。我们确实提供了低级工具,用于检查每个prim的逐弧索引,但仅从弧结构中重建意图和资产身份可能很困难。AssetInfo提供了一种机制,用于识别和定位在组合(甚至阶段扁平化)中幸存下来的资产,以允许客户发现资产被引入的名称空间位置,以及通常如何构造对资产的引用。
以USD文件形式编写的assetInfo是提供给客户端应用程序使用的咨询数据。在阶段加载/合成期间,它不会被USD核心以任何方式咨询或消耗。
AssetInfo字典可以包含客户端或管道发现有用的任何字段,但促进了四个核心字段,每个字段都有直接的API:
- identifier (asset)
提供了一个资产标识符,可以用来用复合弧线来定位资产。 - name (string)
提供资产的名称,因为它将被识别,例如,在资产数据库查询中。 - payloadAssetDependencies (asset[])
为动态资产依赖分析提供了实质性的优化。如果您的资产构建/发布流程可以在发布时预先计算资产有效载荷中包含的外部资产依赖,那么拍摄/渲染时依赖分析(用于资产隔离)不需要加载有效载荷。 - version (string)
提供已成为/可能成为目标的资产的版本。在某些管道中,将资产的修订控制版本注入已发布的资产本身可能是有意义的。在其他管道中,版本是在外部数据库中跟踪的,因此当我们添加对资产的引用时,必须在引用上下文中添加版本assetInfo。
上面例子的延续,说明了程序集:
def Xform "Forest_set" (
assetInfo = {
asset identifier = @Forest_set/usd/Forest_set.usd@
string name = "Forest_set"
}
kind = "assembly"
)
{
# Example of an asset that embeds the '@', and so must be delimited
# with the "@@@" form
asset primvars:texture = @@@body_decal.exr@v3@@@
}
已发布的程序集资源的AssetInfo
资产解析(Asset Resolution)
资产解析是将资产路径转换为可消费资源位置的过程。USD提供了一个用于资产解析的插件点,ArResolver接口,客户端可以使用他们需要的任何逻辑和外部输入来解析在USD场景中发现的资产。这个插件还允许客户端向美元提供资产的数据,而不需要将数据读取到磁盘上。
如果没有可用的资产解析器插件,USD会退回到默认的解析器实现,使用搜索路径来定位通过相对路径引用的资产。请参阅参考资料中的示例。
属性(Attribute)
属性是大多数USD场景中最常见的属性类型。一个属性可以是USD提供的合法属性typename中的一个,并且可以是默认值,也可以是任意数量timesample中的一个值。在任何给定的timeCode解析属性将产生单个值或不产生值。属性根据“最强获胜”规则进行解析,因此任何给定属性的所有值都将从提供默认值或timessamples的最强PrimSpec中获取。请注意,这个简单的规则在有编辑剪辑的情况下会稍微复杂一些。可以通过UsdAttribute API与属性进行交互。
一个简单的例子,一个属性在同一个primSpec中同时拥有一个授权默认值和两个timessample:
def Sphere "BigBall"
{
double radius = 100
double radius.timeSamples = {
1: 100,
24: 500,
}
}
同时带有Default和timessamples的属性
属性块(Attribute Block)
与通过组合重写意见来禁用prims的方式类似,属性产生的值可以通过重写意见None来阻止,该值可以使用UsdAttribute::Block来创建。当然,一个区块本身可以被更强有力的意见所推翻。下面的示例扩展了前面的属性示例,添加了一个DefaultBall prim,它阻止了它从BigBall引用的radius的值,导致radius的值在ustimecode t时解析回退(任何没有回退的阻塞属性在我们调用UsdAttribute::Get时将报告它没有值):
def Sphere "BigBall"
{
double radius = 100
double radius.timeSamples = {
1: 100,
24: 500,
}
}
def "DefaultBall" (
references = </BigBall>
)
{
double radius = None
}
DefaultBall阻止从BigBall引用的半径值
除了完全阻塞属性值之外,还可以通过阻塞单个时间样本来单独阻塞子时间段。考虑下面的例子:
例子1:
def Sphere "BigBall"
{
double radius.timeSamples = {
101: 12,
102: None,
}
}
对于BigBall的属性半径:
- Usd. attribute . get (t)将返回12。TimeCode t in(-∞,102)。
- Usd. attribute . get (t)将返回12。TimeCode t in[102,∞]。
请注意,每个时间样本阻塞能力不允许我们稀疏地覆盖时间样本,即在以下示例中:
def Sphere "BigBall"
{
double radius.timeSamples = {
101: 1,
102: 2,
}
}
def "DefaultBall" (
references = </BigBall>
)
{
double radius.timeSamples = {
101: None,
}
}
对于DefaultBall上的属性半径:
- Usd. attribute . get (t)将返回None。TimeCode t in(-∞,∞)。
属性连接(Attribute Connection)
见连接
属性可变性(Attribute Variability)
见可见性
变更处理(Change Processing)
变更处理是 UsdStage 对构成其组成的任何层的编辑做出响应的操作。在变更处理过程中,阶段上的引语可能被重新索引或完全消失,也可能产生新的引语。了解变更处理的关键是:
- 它总是活跃的。每当你使用Usd或Sdf api来改变任何对UsdStage构成有贡献的层时,该阶段将立即更新自己,在执行创作的同一线程中(尽管更改处理本身可能会产生工作线程),因此它总是向客户端呈现准确的结果。
- 当一个阶段完成一轮更改处理时,它将向已注册的客户端发送通知,以便他们可以根据对该阶段的授权更改保持自己的更新。
监听UsdStage通知的客户端不应该在收到通知后立即更新自己,而是注意哪些已经过时(脏),并推迟更新,直到必要时;这有助于在快速执行许多编辑时将应用程序需要承担的工作量最小化。
- 对于所有对给定的 UsdStage 有贡献的层,一次只能有一个线程在编辑这些层中的任何一个,当编辑和变更处理发生时,没有其他线程可以从该阶段读取数据。这是因为我们不希望使用任何类型的锁来减少对UsdStage数据的读访问。
片段(Clips)
见 值片段
集合(Collection)
集合建立在关系的基础上,以识别USD场景中的对象。一个关系(通过获取它的目标来解析)仅仅是一个识别场景中其他对象的有序路径列表,而一个集合使用一对关系和额外的规则,通过识别一组路径以分层方式包含在集合中,以及一组单独的路径以分层方式排除,来紧凑地编码潜在的大型对象集。例如,下面的集合列出了组成两栋大楼的所有底漆,但每栋楼的13层底漆下的所有底漆除外。
over "World"
{
over "Buildings" (
prepend apiSchemas = "CollectionAPI:luckyBuildings"
)
{
uniform token collection:luckyBuildings:expansionRule = "expandPrims"
rel collection:luckyBuildings:includes = [
</World/Buildings/Skyscraper>,
</World/Buildings/Pyramid>,
]
rel collection:luckyBuildings:excludes = [
</World/Buildings/Skyscraper/Floor13>,
</World/Buildings/Pyramid/Floor13>,
]
}
}
集合通过包含和排除越来越精细的分支来细化层次结构集
从这个例子中我们可以看到,USD中的集合被表示为“多应用”API模式,这样我们就可以在场景中添加任意数量的不同集合。expansionRule属性指定如何扩展包含关系的目标;我们可以包括所有的引物,所有的引物和属性,或者只是目标对象本身。除了prims和属性,一个Collection还可以包含另一个Collection,这允许我们在发布资产时隐藏资产的内部细节,因为消费资产的聚合场景可以绑定材料,并通过针对资产的集合(将资产分解为有意义的组)与其他集合来照亮场景。
我们使用UsdCollectionAPI创建和查询集合。
有关集合(包括使用路径表达式确定集合成员资格的基于模式的集合)的更多详细信息,请参见集合和模式。
组件(Component)
组件是一种“叶子”类型的模型。组件可以包含子组件,但不能包含其他模型。组件可以引用作为模型发布的其他资产,但是它们应该覆盖被引用元素的类型为“子组件”。
def Xform "TreeSpruce" (
kind = "component"
)
{
# Geometry and shading prims that define a Spruce tree...
def "Cone_1" (
kind = "subcomponent"
references = @Cones/PineConeA.usd@
)
{
}
}
一个“组件”资源,在场景描述中声明,覆盖“嵌套”资源引用的类型
见: 模型层次
组合(Composition)
组合是通过相互关联的构图弧线将多个图层组装在一起的过程,从而产生UsdPrims的UsdStage场景。每个UsdPrim包含一个索引,允许客户端随后从相关层提取属性和元数据的“解析值”。合成发生在第一次打开UsdStage时,在舞台上加载或卸载prims时,以及对舞台有贡献的图层被编辑时。合成的结果之一是路径转换,这样所有层中的所有数据都可以通过它们的“场景级”命名空间位置(路径)来访问。
我们有时也会说“一个组合”或“一个组合的整洁”或“一个组合的场景”,在这些上下文中,我们指的是表演组合的结果。
合成操作符(Composition Arcs)
合成操作符是允许USD创建包含“基础”场景描述和覆盖混合的多层丰富组合的“操作符”。这些操作符是:
-
子层
-
继承
-
变体
-
引用
-
负载
我们将这些操作符称为弧,因为每个操作符都针对一个层、一个prim或层和prim的组合,当绘制prim的索引以理解场景或值是如何组成的时候,组合操作符表示将层和primSpecs组合成一个有序图的方向弧,我们在执行值解析时遍历这个有序图。除了subLayers,所有的复合弧线都以LayerStack中的特定质点为目标,并允许重命名该目标质点,因为结果“流过”弧线。USD执行所有必要的路径转换,以确保阶段级查询始终在阶段级名称空间中工作,而不管有多少不同和/或嵌套的组合弧和名称更改对结果有贡献。
除了SubLayers,所有的复合弧线都是列表可编辑的,这意味着layerStack中的每一层都可以稀疏地前置,追加,删除或重置目标,这使我们能够无损地编辑场景的复合结构,因为它演变或通过管道的多个阶段。以下是列表编辑引用的示例。/MyPrim上引用的解析值,将在合成SubLayers时被消耗。Usd是一个双元素列表:[@file1.usd@ @file3.usd@]文件base.usd的内容
#usda 1.0
def "MyPrim" (
references = [
@file1.usd@,
@file2.usd@
]
)
{
}
文件SubLayer.usd的内容
#usda 1.0
(
subLayers = [
@./base.usd@
]
)
# Removes reference to file2.usd, while adding a reference to file3.usd at the end of the list
over "MyPrim" (
delete references = [ @file2.usd@ ]
append references = [ @file3.usd@ ]
)
{
}
Crate文件格式(Crate File Format)
定义(Def)
默认值(Default Value)
直接观点(Direct Opinion)
回退(Fallback)
平整(Flatten)
几何图元(Gprim)
组(Group)
Hydra(Hydra)
索引(Index)
继承(Inherits)
Instanceable(Instanceable)
实例化(Instancing)
插值(Interpolation)
IsA Schema(IsA Schema)
种类(Kind)
层(Layer)
层偏移(Layer Offset)
层堆栈(LayerStack)
列表编辑(List Editing)
LIVRPS强度排序(LIVRPS Strength Ordering)
加载/ 卸载(Load / Unload)
局部化(Localize)
元数据(Metadata)
模型(Model)
模型层级(Model Hierarchy)
命名空间(Namespace)
观点(Opinions)
Over说明符(Over)
路径(Path)
路径翻译(Path Translation)
有效载荷(Payload)
Prim(Prim)
Prim定义(Prim Definition)
PrimSpec(PrimSpec)
舞台上的每个组合Prim都是潜在的许多PrimSpecs的结果,每个PrimSpecs都为合成结果贡献了自己的场景描述。PrimSpec可以被认为是“层中未组合的PrimSpec”。与组合PrimSpec类似,PrimSpec是属性数据和嵌套PrimSpec的容器。重要的是,复合弧线只能应用于PrimSpecs,而那些指定目标的弧线是针对其他PrimSpecs的。
PrimStack(PrimStack)
Primvar(Primvar)
Property(Property)
PropertySpec(PropertySpec)
PropertyStack(PropertyStack)
Proxy(Proxy)
PseudoRoot(PseudoRoot)
Purpose(Purpose)
References(References)
Relationship(Relationship)
Root LayerStack(Root LayerStack)
Schema(Schema)
Session Layer(Session Layer)
Specializes(Specializes)
Specifier(Specifier)
Stage(Stage)
Stage Traversal(Stage Traversal)
Subcomponent(Subcomponent)
SubLayers(SubLayers)
TimeCode(TimeCode)
TimeSample(TimeSample)
Typed Schema(Typed Schema)
User Properties(User Properties)
Value Clips(Value Clips)
Value Resolution(Value Resolution)
Variant(Variant)
VariantSet(VariantSet)
Visibility(Visibility)