Q100517:通过叶级(实例 ID)实例化提高场景图烹饪性能

概括

在任何包含重复几何体的场景中,实例化都能有效降低内存占用并提升场景图的计算性能。通常,当多个副本的几何体完全相同,且仅需进行变换时,实例化效果最佳。本文介绍了一种使用实例 ID 属性的实例化方法,称为叶级实例化,并举例说明如何使用该方法将几何体实例化为点云中的点。

有关其他可用实例化方法的概述,请阅读Katana开发人员指南中的以下部分:实例化

请参考 Katana 项目文件“ PointcloudInstancingAndMaterialAssign_Arnold.katana ”,其中包含一个示例项目,用以说明以下关于点云实例化的部分。

叶级实例化

以下部分简要概述了叶级实例化。如需更深入地了解实例化,请参阅RenderMan 文档:RenderMan 26 文档 - Katana中的实例化,其中演示了如何设置所有三种实例化方法,作为实例化的入门介绍。

在叶级实例化中,预先存在的重复几何体可以被实例化,这意味着场景中已多次存在的几何体只需加载一次,之后任何重复出现的几何体都将引用内存中的同一组几何体数据。对于现有的Katana项目,如果其中包含可从实例化中受益的重复几何体,只需在场景图中的重复几何体位置添加instance.ID属性即可轻松修改。然而,缺点是只有没有子元素的几何体位置才能被实例化。

为了实现这一点,所有相同的位置都应具有匹配的instance.ID属性。渲染器会选择一个位置作为实例源,然后将所有其他位置视为该位置的实例。

注意:了解如何选择几何体作为实例源至关重要,这决定了场景图中所有位置是否都需要包含几何体,还是只有最高位置需要包含几何体。请参阅渲染器文档以了解如何选择实例源。

叶级实例化到点云

使用实例 ID 将实例化到点云需要使用 OpScript 将实例源复制到点云的每个点。更简单的解决方案是使用分层实例化,无需 OpScript(参见Q100508:使用分层(实例源)实例化提高场景图计算性能)。然而,无论采用哪种实例化方法,使用 OpScript 的好处在于,各个实例的位置在查看器中可见,并且可以为每个实例单独分配材质。潜在的缺点是场景图会略显臃肿,尤其是在存在数千个实例的情况下。

OpScript 会生成一个实例位置,并复制点云中每个点的instance.ID属性。OpScript 生成位置后,渲染器会选择一个位置作为几何源,然后将所有其他位置视为该位置的实例。以下步骤实现了点云的叶级实例化。

1. 使用 Alembic_In 导入点云和实例源。然后将点云位置上的instance.ID属性设置为任意字符串值。在本例中, instance.ID设置为bob

2. 创建一个 OpScript 节点,并将其location参数设置为场景图中将生成实例的位置,例如: /root/world/geo/derivedassets

3. 在 OpScript 节点中,创建一个user.instanceSourceLocation参数,并将其设置为实例源在场景图中的位置。然后创建一个user.pointCloudLocation参数,并将其设置为点云在场景图中的位置。有关创建用户参数的步骤,请参阅Katana 用户指南:添加用户参数。请注意,在叶级实例化中,实例源位置不能包含子节点,否则实例化将无法正常工作。

4. 然后将以下代码复制到 OpScript 节点的脚本参数中,为点云中的每个点生成实例位置。

 -- Read op arguments
local instanceSourceLocation = Interface.GetOpArg( "user.instanceSourceLocation" ):getValue()
local pointCloudLocation = Interface.GetOpArg( "user.pointCloudLocation" ):getValue()

if Interface.AtRoot() then
 
-- Read the point cloud
 
local points = Interface.GetAttr( "geometry.point.P" , pointCloudLocation):getNearestSample(Interface.GetCurrentTime())

 
-- Read the instanceID
 
local instanceID = Interface.GetAttr( "instance.ID" , pointCloudLocation):getNearestSample(Interface.GetCurrentTime())

 
-- Loop over points
 
local x, y, z
 
local gb = GroupBuilder()
 
for i= 0 , #points/ 3 - 1 do
      x = points[
3 *i+ 1 ]
      y = points[
3 *i+ 2 ]
      z = points[
3 *i+ 3 ]

     
-- Build op arguments for the child location
      gb:update(Interface.GetOpArg())
      gb:set(
"childAttrs" , Interface.GetAttr( "" , instanceSourceLocation))
      gb:set(
"childAttrs.geometry.instanceSource" , StringAttribute(instanceSourceLocation))
      gb:set(
"childAttrs.xform.interactive.translate" , DoubleAttribute({x, y, z}))
      gb:set(
"childAttrs.instance.ID" , StringAttribute(instanceID))

     
-- Create the child
      Interface.CreateChild(
         
string . format ( "child%04d" , i),
          Interface.GetOpType(),
          gb:build())
 
end
else
 
local childAttrs = Interface.GetOpArg( "childAttrs" )
 
for i= 0 , childAttrs:getNumberOfChildren() -1 do
      Interface.SetAttr(childAttrs:getChildName(i), childAttrs:getChildByIndex(i))
 
end
end

上述 OpScript 将实例源复制到点云中的每个点,包括instance.ID属性。

5. 最后,使用修剪节点从场景图中移除点云,使其不参与渲染。这一步是必要的,可以防止渲染器将该点云选为实例源。

材料差异与材料分配

逐个实例修改材质会增加一些额外的工作量,因为所有实例都会继承实例源的属性,例如材质。本节将介绍一种在保持实例化性能优势的同时,为每个实例更改材质和着色器的方法。

使用 OpScript 进行叶级和分层实例化,为点云上的每个点创建实例位置,其优点是可以为每个实例位置分配材质。

生成实例位置后,可以针对每个实例的不同位置设置不同的材质。要识别特定实例,只需在场景图中展开实例位置,然后在查看器中选择所需的实例即可。在提供的示例项目中,实例位置位于/root/world/geo/derivedassets目录下。

然后可以使用 MaterialAssign 节点或在实例位置上设置materialAssign属性,指向材料位置,将材料分配到所需的位置。

延伸阅读
有关实例化的概述,请参阅下方第一个资源链接。下方的其他资源将深入探讨每种实例化方法的其他高级实例化主题。

附件

我们很遗憾听到

请告诉我们