Houdini 20.0 Geometry

Packed primitives

On this page

Overview

Packed primitives express a procedure to generate geometry at render time. The purpose is to decrease the amount of memory used when interacting with Houdini by reducing duplication and only loading information when necessary.

Packed primitives have information about geometry embedded inside of them. The information could be an actual piece of geometry stored in memory, a reference to a part of another piece of geometry, or a file path to geometry stored on disk. Mantra, the Houdini viewport, the solvers, etc, know how to interpret the packed information, and can render/display/work with the geometry efficiently.

Packed primitives cannot be edited – they are lightweight references. If you want to edit packed geometry, you have to use the Unpack node to extract the part of the geometry you want to edit, modify the geometry, and then optionally repack the geometry using the Pack node.

Packed primitives are useful for rendering and simulating heavy geometry or large numbers of copies/instances. Any time the geometry will not change (for example, non-deforming RBD objects), you can benefit from packing the geometry.

Types of packed primitives

In-memory packed primitives

You get an in-memory packed primitive by converting geometry to a packed primitive using the Pack geometry node. This creates a packed geometry primitive with an embedded reference to the current version of your geometry in memory. The embedded geometry becomes a single un-editable primitive with a single transform.

The embedded geometry is a reference to content in memory. Copying a packed primitive copies the reference rather than the geometry itself, so the referenced geometry is shared among all copies of the packed primitive. This is more memory efficient than copying unpacked Houdini geometry, which creates independent duplicates of all points, primitives, attributes, etc.

Copies of packed primitives use less memory, are simpler to transform, and can be drawn more efficiently in the viewport or rendered by Mantra.

Because the referenced geometry exists in a traditional network, you can easily generate procedural geometry which adapts to your scene, use stamping to generate variations of your packed geometry, or make interactive edits to your geometry while viewing the results live. Essentially, working with in-memory packed primitives is a more interactive and user-friendly version of traditional instancing workflows.

You can unpack individual copies of an in-memory packed primitive in a geometry network to create an actual copy of the referenced geometry. This allows you to generate procedural workflows which are a hybrid of traditional Houdini geometry and packed primitives.

Packed in this case does not mean compressed or smaller. You are keeping the original geometry in RAM, as well as using a bit of memory for each reference. A single packed primitive is not necessarily more efficient than using the original piece of geometry. The benefit comes from the efficient representation of large numbers of copies that share the referenced geometry. This is important to remember when copy-stamping packed geometry. If every instance of your packed geometry is unique, there are no memory or performance benefits. In fact, this will use more memory than “real” geometry, because each packed primitive has its own overhead. (However, it is possible to offset the cost of packing stamped geometry somewhat when there are limited numbers of stamped variations. See the Copy SOP's Cache Stamping Geometry parameter.)

Packed disk primitives

A packed disk primitive embeds a reference to a file on disk. At display or render time, Mantra/Houdini reads the data from disk rather than keeping it in memory. Some file formats, such as .bgeo and Alembic, make this very efficient by allowing fast random access to their contents. Because packed disk primitives simply load already-generated data from files, they are less dynamic than in-memory packed primitives. The only way to edit a packed disk primitive is to unpack it, copying the file data into memory.

You can load geometry from disk as a packed primitive by using the File SOP’s Load parameter set to Packed Disk Primitive.

A packed disk primitive is similar to an in-memory packed primitive in that the embedded geometry appears as a single un-editable primitive with a single transform. Much like in-memory packed primitives, a packed disk primitive is an excellent choice for efficiently creating copies of geometry in the viewport and in Mantra. Copying a packed disk primitive copies the reference to the disk file. The viewport does not copy the geometry for each instance, but simply draws the same data multiple times with different transforms. The viewport can also draw a much simpler representation of the referenced geometry, such as a point cloud or bounding box.

Like Houdini, Mantra can “stream” the data from the disk file as needed, instead of copying the data into memory. This reduces Mantra’s memory usage. For in-memory geometry, Houdini writes the entire geometry into the IFD file (the scene description file it sends to Mantra). For packed disk primitives, on the other hand, Houdini writes the reference to the file on disk, making IFDs smaller on disk and much faster to generate for very large/complex scenes.

Packed disk primitives are ideal for scene assembly, especially for static background objects. Their small memory usage at render time also makes them very useful for objects with large on-disk footprints, such as simulation output.

Packed Disk Sequence primitives

Packed Disk Sequence (PDS) primitives are similar to Packed Disk primitives (see above), but the primitive references a sequence of geometry filenames and an index into the file sequence. When Mantra loads the sequence primitive as part of the scene, it knows the full sequence (instead of just having the geometry for the current frame), so it can interpolate between frames for motion blur. In this way, Packed Disk Sequence primitives are a simple way to instance animated geometry (in the form of per-frame geometry files) efficiently at render time with motion blur.

To import an animated sequence as a PDS, use a File SOP. Set the Load parameter to Packed Disk Sequence. Click the file chooser icon next to the Geometry File parameter and choose the geometry sequence to load.

Note

When you are loading a PDS, the $F in the Geometry File pattern is interpolated between the values in the Frame Range parameter (it does not refer to the current frame as usual). We use $F here for consistency with the other modes, so the file chooser works as you expect.

The Sequence Index parameter on the File SOP sets the (floating point) frame to use in the animated sequence. The default is $FF - 1. You can edit the index on an existing PDS primitive using the Packed Disk Edit SOP.

Packed disk primitives automatically cycle when the rendered frame is outside the animation’s frame range. You can change this by setting the Wrap Mode on the File SOP to one of Clamp, Cycle, Mirror, or Strict:

  • Cycle: Automatically wraps the sample index to the valid range (the default behavior).

  • Clamp: Clamps out of range index samples to the valid range. For example, if the valid frame range is 1-5, frame numbers greater than 5 will stick at frame 5.

  • Mirror: Wraps by reversing in a zig-zag or ping-pong style.

  • Strict: Gives empty geometry outside the valid frame range.

Packed fragments

When you pack geometry that includes a name attribute, each piece of geometry that shares the same name value becomes a packed fragment primitive containing a reference to the original geometry. Each fragment shares the same geometry, but refers to a subset of the geometry. If you unpack a fragment, only that part of the original model is copied into memory.

Packed fragment primitives are ideal for representing many pieces of a complete model, especially when each fragment will receive some unique transformation, such as in a rigid body simulation.

Because each fragment is just a reference to the original geometry, it is very efficient to use fragments when you have a large number of them. However, if you delete many of the fragments, their use can become more inefficient than using real geometry, because Houdini continues to keep the entire original model in memory. With real geometry, memory usage would be high at first, but would decrease as you delete parts of the geometry. You can try getting the best of both worlds by unpacking the fragments when you have only a few remaining.

How to

To...Do this

Convert SOP geometry into a packed primitive

Use the Pack SOP. The Pack node can create a new primitive with all the input geometry, or separate packed primitives based on the value of an attribute (such as name, as created by Shatter).

Extract a “sub-primitive” from inside a packed primitive

Use the Unpack SOP.

Import geometry from a dynamics network as a packed primitive

The DOP Import SOP has an option to import Geometry data from a dynamics network as a packed primitive.

Access attributes on packed geometry in a VEX shader

You can use the renderstate VEX function to get the value of attributes on packed geometry. For example, if the packed geometry has a Cd attribute, you can use renderstate("packed:Cd", PackedCd).

Note

You cannot set/use primitive attributes on the geometry other than the material attribute (which Houdini treats as a special case). In general, primitive-level attributes will not work since Houdini treats packed geometry like a single primitive with a single point.

Rendering

Packed primitives are extremely useful for rendering in Mantra. They let you generate IFDs, render faster, and use less memory and disk space.

Material assignment

With standard geometry, you can assign materials at two levels:

  • At the object level, in the Geometry node's parameters.

  • At the geometry (SOPs) level, using the Material node to set the material attribute on certain primitives. This overrides the object material for the primitives that have it.

When Houdini generates the scene description (IFD) file for rendering, it checks the object and geometry attributes for material assignments, so it knows which shaders to include in the IFD.

When you use packed primitives, there is a third possible level of material assignment:

  • Material attributes on the embedded geometry inside packed primitives. These override the “higher” levels (primitive attributes and object materials).

However, when Houdini generates the IFD, it does not look in the embedded geometry (which might be a very large file that would be slow to scan through). As a result, Houdini does not know the embedded geometry’s material attributes, and will not know to include the shaders in the IFD. Only when Mantra unpacks the primitives at render time will it find out that it may not have the shaders it needs.

To work around this problem, you can tell Houdini to include all shaders in the scene in the IFD, regardless of whether they are assigned at the object or geometry levels. Turn on Save all SHOPs on the Mantra render node. (This will increase the on-disk size of your IFD by a small amount). As long as you load the shaders that are needed by packed primitives into the scene, they will be available at render time.

Tip

For information on how to assign shaders and override shading parameters inside packed geometry, see material style sheets.

Displacement and subdivision surfaces

Houdini performs displacement shading and subdivision surface rendering the same way for packed geometry as for standard geometry. However, if you are primarily using packed geometry for instancing, you need to think about dicing.

Before rendering a displaced or subdivided surface, Mantra “dices” the geometry into smaller primitives until there is one primitive for every pixel (when shading quality is set to 1). This means that it dices objects closer to the camera more than objects in the distance (which have less pixel coverage).

When instancing using packed geometry, this can cause a problem. The benefit of instancing is that geometry is shared across all instances. However, if you add displacements or subdivision rendering, Mantra must load and dice each object individually, which means the geometry is no longer being shared.

To avoid this problem, you can add the vm_sharedisplace render property to the object containing your instances. Turning this parameter on will tell Mantra to:

  • Use the highest level of dicing necessary for the scene on one instance, and then

  • Share that diced geometry between all instances

This means that objects that are far away will get “too much” detail, which could potentially cause slowdowns. However, the benefits of preserving instancing likely outweigh any downside.

In the worst case, if “incorrect” dicing levels cause problems, you can split the instances between two objects based on distance from the camera. In this case, the highest dicing level is computed separately for near and far.

Alternatively, you could unpack instances close to the camera, removing them from the “highest necessary dicing level” calculation.

Attributes

Disk and in-memory packed primitive instances are simply pointers to the same file or memory, so each instance cannot have individual attribute values (except the material and vel (velocity) attributes which are specifically hacked to work, see below).

Packed fragment instances can have individual attributes because they are coalesced, but this also means that they are less efficient than on-disk or in-memory packed primitives.

Alembic primitive instances cannot have individual attribute values. However, there is an option to “unshare” Alembic primitives in Mantra. This uses a lot more memory but allows you to have individual attributes on Alembic primitive instances at render time.

Mantra creates a tree of virtual Mantra objects for packed primitives, and copies the material attribute down to each virtual object in the tree (if they do not have a material attribute of their own). In this way, materials on the Geometry object are properly applied to packed primitives inside. Similarly, the vel (velocity) attribute is added down through the virtual object tree, so motion blur will work properly.

Primitive attributes on packed primitives are copied to object properties on the virtual Mantra object. You can access them in shader code using the Render State VOP or renderstate VEX function.

If you have a primitive material attribute inside a packed primitive, Houdini will not look inside the primitive to determine that it needs to include that material in the file it sends to Mantra. You can fix this (at the expense of a larger render file) by turning on the Declare all SHOPs option, which tells Houdini to include all materials in the render file.

When Mantra renders packed primitive fragments, it copies attributes from the packed primitive onto the geometry, so velocity blur on fragments will work. Other packed primitive types do not work this way since they are rendered as instances.

Geometry

Understanding

Modeling

Terrain

Fracturing

  • Destruction

    How to break different types of materials.

Clouds

Next steps

Guru level