Copy to Points

The Ultimate Houdini node reference

Visit the Node Bible

To learn more, please log in or sign up for free to explore the Node Bible.
Write your awesome label here.

Copy to Points

Write your awesome label here.
Write your awesome label here.
Write your awesome label here.

This node will spawn objects onto points.

Drag to resize


    Copy to points is perhaps the most commonly used, most important, and most confusing nodes in all of Houdini.  The reason why it's so complicated is because you need to understand vex, packed primitives, and quaternions/matrices all at once to control and use it properly.

    For anyone new to Houdini, this will absolutely become an area of struggle at first, and it requires patience to learn everything properly.  To make things more complicated, this is also an area of constant change with each release of Houdini, and some of the videos out there may be utilizing old workflows.  In this node bible entry, we are going to simplify the process while providing a simple and practical glimpse at the latest workflows.

    In order to understand instancing better, however, it is required that you understand what packed geometry is.  This is a topic which I cover extensively in Destruction I, so please visit the link if you do not understand what "packed primitives" are.

    In addition, I would recommend obtaining some basic skills in vex before studying the topic of instancing.  Much of what we will learn here deal with the attributes which are being understood by the copy to points.  So, it's worth visiting Vex Foundations I for a base understanding of that as well.  If you are an absolute beginner at Houdini and are unfamiliar with what attributes are, then you'll want to visit Houdini for The New Artist I and Houdini for The New Artist II before studying this topic. 

Assuming that you are caught up on all these topics, let's check it out.

Drag to resize

Main Parameters:

To begin, we will look at the main parameters which are present on the copy to points node.

Source Group:

--  The first input on the copy to points will contain the geometry that you would like to copy over.  The source group allows you to specify a group of points or primitives which belong to the geometry that's connected to the first input.

Source Group Type:

--  Be sure that you specify the correct group type.  When using the Source Group parameter above

Target Points:

--  The second input on the copy to points node contains the points that you would like to copy geo to.  The "Target Points" allows you to specify a point group which belongs to the second input.  Only points in that group will be candidates for receiving geometry.

Piece Attribute:

--  This parameter is very useful when instancing.  Let's say that you have three different trees and you want to copy over one of the three trees randomly to a point.  The "Piece Attribute" allows you to do this.  To make this work you need to match an attribute on your points with an attribute on the geo that you're copying over.  So, if a point has an attribute called "variant" and it's = 1, it's going to look for a tree with an attribute called "variant" that also equals 1.  If it finds a match, then that's the tree which gets copied over.  

     To make the most of this parameter, it's best to use this in combination with the Attribute Randomize node set to either Uniform (Discrete) or Custom (Discrete) on your points.  Also keep in mind that the copy to points expects this attribute to be an integer or a string.  This will not work if your attribute is a float.  This is important to keep in mind because the attribute randomize will offer you a float attribute, and you'll need to convert that into an integer for this to work.  The easiest way to do that is by using a point wrangle after the attribute randomize and saying something like:

i@variant = f@randoVariant;
With @randoVariant being the attribute that you generated with the attribute randomize sop.

    In addition, you'll need to have a variant attribute set to the primitives that you'll be copying over.  An easy way of doing this is by using the Connectivity SOP and changing the "class" attribute to "variant".  It does not matter whether or not the attribute is set to primitive or point on the geo that you wish to copy over.  The copy to points will understand both attribute types.

Pack and Instance:

--  This will convert the copied geometry into packed primitives.  NOTE:  These packed primitives do not include a @path attribute and it requires more memory than simply packing your geo with the pack node before copying it to points...  That being said, it is generally advised to pack your geometry before copying them over to points while leaving this box unchecked.  In addition to a lower memory footprint, the @path attribute is preserved.

Transform Using Target Point Orientations:

--  This tells the Copy to Points node to rotate the copied geometry to face the target point normals.  NOTE:  If a normal is facing upwards in the y-direction, then the geometry will look in the y direction as well.  It will NOT sit on top of the normal.  Please see the image below:
This leads us to the first question everyone always asks about the copy to points node:  "How do I get the pig heads to lie on top of the geometry?"  More on that question in a moment... 

For now, also know that the "Transform using point orientations" allows you to use other attributes to control how the pigs are spawned to the points.  These attributes live on the points and it includes:  @orient, @pscale, @rot, @up, @v, @scale, @P, @trans, @pivot, @transform, @shop_materialpath, and @material_override.  More on what these attributes do are described in the below section...

Back to the question of:  "How do I get the pigs to sit on top?"  One way to do this is by simply rotating the pig head as you can see below:
However, this method is not ideal because that means you need to rotate objects by 90 degrees and move them by the z bbox each time.  So, instead, we have an attribute to use along with @N called @up.  According to the user documentation (found here: )  the @up attribute represents the y-direction on the pig head.  So if we go back to our original position of the pig head, it looks like this...
Wouldn't it be nice if we could just swing that Z axis upward towards the y axis?  It turns out we can! - with the help of the dihedral() function in vex.  It sounds really fancy, but all it does is this:
Basically put, we're just trying to swing that normal around in 90 degrees with the dihedral function.  But keep in mind that we're not adjusting the normals on the pig head... those will stay the same.  We're trying to rotate the normals on the points which belong to the grid!  So here's what this looks like:
The dihedral function requires two things:  The original axis that you're trying to rotate, and a vector which acts like a handle that we can use to twist the original axis by 90 degrees.  In my head, I picture twisting a toothpick when using the dihedral function.  The second argument basically draws out the toothpick, and you're twisting that original vector 90 degrees to the right.  This will swing that @N around to give us this:
Great!  Now we've specified a new @N that allows Mr. Pighead to sit on top of the grid.  There's just one problem...  the @up vector will default to {0,1,0} unless you tell it otherwise. The reason why this is a problem is because of situations like this...
The piggie might seem fine, but watch what happens when we use the old @N vector as the @up value...
This problem becomes really noticeable if we use a sphere..
So now we are more enlightened about the "Transform Using Target Point Operations."  @N, @up, and the dihedral function are what we've discussed thus far, but we'll get to those other attributes later.  For now, let's move on to the rest of the copy to points parameters and then come back around to the other attributes.

Transform Using Implicit Target Point Normals if No Point N Attribute:

--  If there is no N attribute present, then this tells the copy to points to use the "implicit" target normals.  It's basically like setting down a normal sop if this node doesn't find any @N attribute.

The attributes from target section deals with the transfer of attributes from the points to the geo.  If, you want the geo to adopt or combine with any attributes from the points, then this is where you can do that.

Reset Attributes from Target:

--  This reverts the attributes from target to its default settings.

Attributes from Target:

--  How many operations would you like to do?
--  Apply to: is asking you what kind of attribute you would like to affect on the geo.
--  by:  Allows you to copy, do nothing, multiply, add, or subtract.

Drag to resize

Additional Attributes:


This section describes additional attributes that the copy to points understands when "Transform Using Target Point Orientations" is checked on.  For a list of these attributes, please visit:

After you understand how to use @N and @up, the next thing to figure out is how to rotate objects that are spawned to these points using attributes.  There's two ways to do it - you can use Euler rotations or Quaternion rotations.  This part is really important --- IF you are using @orient, then you are using the quaternion method.  While @orient is present, N, up, and v are ignored.  Otherwise, those attributes, along with any additional Euler rotations, are used to determine where the geo is facing and/or rotated.  For a better explanation between the two, please visit the course - Quaternions & Matrices.


--  The @orient attribute is a vec 4 (quaternion) attribute.  In vex, you can use p@orient to write out the vec4 attribute.  This @orient attribute will override any Euler rotations which may be present.  This means that N, up, and v are ignored as soon as you use this attribute.

 This attribute often gets used along with the quaternion() function to build out this vector 4 attribute. 

For most situations, this is what you want to do if you're using the quaternion method...

1.  Orient the instances properly
    *  The best way to do this is by using the old @N and @up attributes to create a rotational matrix that then informs the @orient where to go.
    *  In vex, you'll write out... p@orient = quaternion(maketransform(@N,@up));
    *  This is like saying... "Hey I want to use quaternions to orient my instances (p@orient).... I want to create vec4 data using the quaternion() function... and I'm going to tell that ve4 information where to go using by using a matrix whose X,Y,Z axis is determined by @N and @up that I've already figured out.... (maketransform(@N,@up)

As you can see by the pig head sphere below, we're now exactly where we were before when we did the @N and @up example!  But now we're in quaternion land.  Once you're in quaternion land, you can no longer use N, up, or v...
Okay cool, but how do we rotate once we're in quaternion land?  This brings us to step #2...


--  This attribute adds an additional rotation to the geometry after the @orient attribute has been applied.
--  This attribute also expects a vec4 and is best used with the quaternion() function
--  In this case, you'll want to say...

@rot = quaternion(chf("Angle"),@up);
Now with the "Angle" parameter, we can essentially "twirl" the pig head around the @up vector.  Brilliant!


--  Float attribute that adjusts the overall scale of your instances


--  Vector attribute that adjusts the scale of your instances, but this option gives you the option to do so in x,y,z


--  See above sections


--  By default, this is a vector which points in the y-direction and you ought to set this perpendicular to the N in almost all situations.  See the above sections on how to do that.


--  If @N is not present, then @v will be used instead.  This might be useful when, say, you're trying to instance objects onto a pop simulation.


--  Tells the copied geo where to go


--  Vector attribute which adds an additional transformation in x,y,z to the instanced geo.


--  In most situations, it's ideal to make sure that your geometry is being instanced from the origin while sitting on top of the y-axis.  This is because otherwise, when you go to copy over to the points, any translation away from the origin will result in an additional translation away from the point that you're trying to copy to.  

    When it comes to the @pivot attribute - imagine that, by default, this is set to {0,0,0}  If you were to set this to something like {0,-3,0}, then the instances will hover above the surface like so...
Also recognize that rotations will occur around the pivot.  Using quaternion rotations with @orient will not disable the @pivot attribute.  Again, in most situations, it's easiest to keep your pivot at {0,0,0} unless you have a reason otherwise.


--  This can exist as a 3x3 or 4x4 matrix.  This is a transformation matrix which will translate your instances.  To apply a rotational matrix, you'll want to multiply a rotational matrix against @P.  For more information, please visit Quaternions and Matrices


--  This is the path to a shader and can be applied to the instanced geometry as it's copied over to the point.  If, for example, you want the geometry to adopt the material path which is specified on the point, then this attribute is what you'll want to specify on the points.  This, in combination with the Attribute Randomize node set to custom discrete, could allow you to randomly assign shaders to each point if you'd like.


--  "A serialized Python dictionary mapping parameter names to values. You can add this attribute to points using the "override" controls on the Material surface node. When you instance an object onto a point with this attribute, the instanced object applies the given overrides to its material.
Not supported in the viewport."

Drag to resize

Attribute/Transform Priority

In addition to what we discussed above, there was a great post by wolfwood on the SideFX forums which describes attribute priority really well.  See below for what wolfwood has to say:

"If the orient attribute exists, N, v, and up are ignored.
If the orient attribute does not exist:
    *  The geometry's +z-axis is aligned to point down N or v.
If both N and v exist, v is ignored and N is used.
If the up attribute exists then the geo's +y-axis is aligned to it.

Construction of the transform matrix is as follows:
O = orient matrix
S = scale matrix (scale * pscale)
L = alignment matrix (*)
R = rot matrix
T = trans matrix (trans + P)

** The alignment matrix (L) is defined by N or v and up.

IF N exists AND up exists and isn't {0,0,0}:
    L = mlookatup(N,0,up)
ELSE IF N exists:
    L = dihedral({0,0,1},N)
ELSE IF v exists AND up exists and isn't {0,0,0}:
    L = mlookatup(v,0,up)
ELSE IF v exists:
    L = dihedral({0,0,1},v)

IF orient exists:
    Transform = O*R*S*T
    Transform = S*L*R*T

For additional examples, it's also worth checking out the Joy of Vex: