Home Creators Posts Import Register Favorites Logout

Downloads

Content

Gaffer offers a full set of tools for instancing geometry, but the workflow can feel a little unintuitive at first. Let’s demystify it!

Before we start, a few words about instancing. It’s a frequently-used technique for creating memory-efficient copies of geometry across hundreds, thousands, even billions, of point positions in 3D space. We’ve explored this topic before in the context of USD in general and Houdini in particular.

Gaffer follows the same approach as other DCCs by allowing us to manage the geometry we want to make copies of and the points we’re copying them to. The points carry attributes that control the size, position, orientation, and more, of our instances. Commonly-used attributes include orient (orientation, usually a quaternion), P (position), radius (size, also pscale or width), and Cd or Cs (color).

It’s often enough to simply randomize these attributes within desired ranges, but it’s also common to paint attribute masks for more manual control. Gaffer doesn’t have painting tools (yet!), so that task is best handled elsewhere, such as in Houdini or Blender.

Ideally we would create this complete point cloud in another DCC, export to Alembic or USD, then bring it into Gaffer for final setup and rendering. That being said, for this article I’ll set up a mini point cloud using only Gaffer-native tools, including some OSL (Open Shading Language) code.

Our complete Gaffer workflow will look as follows: create some template geometry, scatter points onto it, add attributes to those points, then instance Gaffer test geometry onto them.

First, add a Plane and give it a large number of divisions.

Next, and this is optional (but fun!), we’ll use OSL to displace the plane into some rolling hills. Connect an OSL Object and attach an OSL Code node.

The code is as follows:

outP = P;outP.z = P.z + (noise("perlin", P x 5) x 0.1);

Here we’re saying that the outP output port on the OSL Code node will equal the existing position of each point, P. We then modify the Z (up and down) value randomly using a noise algorithm. OSL doesn’t have a basic random function, so we have to use noise functions instead. Perlin noise creates nice rolling hills, but here are cell and hash for comparison:

Next, attach a Scatter node to our Perlin Hills. This randomly populates the surface with points that deviate from the strict grid pattern of the original geo. We can set the Density to a fixed number or use a density attribute contained on our geometry (if we had created one elsewhere). Click on Drawing Mode > Points in the Viewer's top menu to see all points. You can hide the plane in the Hierarchy View if you want.

Now we’ll add some attributes to the points. We want random width to control the size of our instances and a random prototypeIndex to control which geometry displays on which point. Note that Gaffer refers to the geometry to be instanced as Prototypes.

I attached new OSL Object and OSL Code nodes to the scattered points and used this code to create two new attributes:

width = mix(.5, 1.5, noise("hash", P));index = int(round(cellnoise(P)));

We use a mix function to clamp our hash noise between 0.5 and 1.5. This means our prototypes will be randomly scaled between 50% and 150% of their original size. The hash noise is a good OSL substitute for a generic random function that you’d find in other programming languages.

The index variable will get assigned to prototypeIndex. Rounding is used to set the cellnoise output to either 0 or 1. To confirm this, select the points in the Hierarchy View and check out the values in the Primitive Inspector. Yep, we’ve got 0 or 1 randomly assigned to each point.

Next, attach an Orientation node. This helps us create random orientation, saving us the trouble of wrangling quaternions manually. Trust me, you don’t want to be wrangling quaternions if you can avoid it.

Here I enabled Random and set up a +/- 180 degree rotation around the Z axis, plus random Spread (tilt) of 10 degrees around that axis. This is all converted to quaternions under the hood so the final instances have something to work with.

And that’s our points all set up. Remember that you’ll most likely be creating a point cloud elsewhere and importing it for Gaffer to use. But it’s nice to know how to use OSL and Gaffer’s native tools to achieve similar results!

Now let’s create our base prototypes. Load two Gaffer test models: cow and robot. Add them to a group called prototypes and find them in a compromising position:

Finally, connect an Instancer node. This will collect all the work we’ve done thus far and create our final instances. We can leave most settings at default, but make sure to add our OSL width attribute under the Scale parameter.

There you go, robots and cows out enjoying a day in the hills! Their size and orientation is set using the attributes we created.

Scenes of this type are easily ingestible into any of Gaffer’s supported renderers: Arnold, Cycles or RenderMan. You can continue to make use of point attributes to drive material parameters and create random variations in colors, textures and more. That brings us out of Gaffer Land and into each renderer’s native workflow, so we’ll save that for another article.

Download the attached .gfr file, give it a test drive and apply the same principles to build your own scenes. Enjoy!

Files

Comments

Simba Kunedzimwe

This is very handy. Thanks for this. How would one use low poly proxies as instances and then render high res geometry at render? I am thinking sort of like the Switch node in Blender Geonodes, which can be set to kind of mimic that, though at the moment it appears flims.y