Home Creators Posts Import Register Favorites Logout

Content

I ran into a problem recently trying to render curves in Cycles in Gaffer with a gradient texture applied from root to tip. Many renderers include a tool for just this task, but Cycles, being in this case somewhat orphaned from its original home in Blender, is missing this feature. I decided to make an implementation using Blender's Geometry Nodes, keeping the entire tool chain open source. Here's how I did it!

Texture coordinates on curves go by many names: curveu, curve:t, parametric UVs, implicit UVs, W Coordinate, and no doubt many more. No matter what you call them, the goal is the same: describing how far each point in a curve is from the starting point.

When working with traditional two-dimensional UVs we are creating coordinates on a image plane with values between 0 and 1 on each axis and mapping those coordinates to points on our geometry. In the case of implicit coordinates on curves we are only mapping a single dimension (hence the "u", "t" or "w" in some of the names above). The diagram below illustrates the concept:

Here we have three curves of different lengths (1 unit, 2 and 2.8), each with 7 vertices/points/control points. The distance of each point from point 0 is written next to it. On the right is our texture coordinate system spanning 0 to 1, illustrated by a black to white gradient.

We can immediately see a problem: any curve longer or shorter than 1 unit will map incorrectly to our coordinate system. This is what curveu et al. aim to solve: fitting our curve into a 0-1 range.

With this in mind we can implement the math in any DCC. Houdini has curveu generation built into its resample node, which makes life easy, but for demo purposes I'll use Blender's Geometry Nodes to create a custom curveu implementation.

Open up Blender and import some curves of your choosing (note that Blender calls them "splines") . Add a new Geometry Node modifier to them:

Add the following nodes:

Resample Curve (optional, use if you want to change the number of points in each curve)

Store Named Attribute

Spline Parameter (get the distance of each point from the start of the curve)

Spline Length (get the total length of the curve)

Map Range

The Store Named Attribute node is where we create our curveu attribute on each point. The Value of curveu is the result of a Map Range node. The Map Range takes the Length at each point in the spline as its input, then maps that value between 0-1 relative to the length of the entire spline, which is plugged into the From Max input.

Essentially, this "squishes" (or expands) the length of each spline to exactly 1 and creates a texture coordinate at the appropriate fractional point along the curve, matching the original ratios perfectly. A point at distance 1 in a curve of length 2 will map to 0.5 on a 0-1 curve, a point originally at 1.26 will map to 1.26/2 = 0.63, and so on.

Looking at the spreadsheet we can see that curveu is now present on each point. We can visualize it using a Viewer node with the Map Range Result connected to it. Attaching a Float Curve is also helpful to even more clearly show the 0-1 gradient.

That's all there is to it!

I exported the curves as USD from Blender and brought them into Gaffer. Then, a simple cyclesAttribute node pointing to curveu and plugged into a cyclesRGBRamp allows for a gradient in melanin concentration in each hair. Beautiful!

Files

Comments

No comments found for this post.