Pine Needles Hair System for Instanced Trees

   452   9   2
User Avatar
Member
5 posts
Joined: Jan. 2016
Offline
Hi folks,

I've been busy with Disney/Pixar's Woodville Art Challenge and one of my mini-sub-challenges has been to build a forest of custom conifer trees for it.

I thought to generate the needles using Hair Generate.

So, I got my fbx tree (coming from Blender) inside Houdini 17.5 and first processed the primitive needles to get single segment polylines instead.


I bet there must be a more optimal way to do that, but given my limited VEX knowledge. I kinda put together this in a wrangle. Do you think I can skip the foreach block? It must be possible to write that directly inside the wrangle…



vector pos0, pos1, norm0, norm1;

int pts[] = primpoints(0, 0);
int count = len(pts);

foreach(int pt; pts)
{
if(pt != @ptnum)
{
if(pt%2==0)
{
int line = addprim(0, "polyline");
addvertex(0, line, @ptnum);
addvertex(0, line, pt/2);
}
pos0 = point(0, "P", 0);
pos1 = point(0, "P", 1);
norm0 = pos1 - pos0;
norm1 = pos0 - pos1;
setpointattrib(0, "N", 0, norm0, "set");
setpointattrib(0, "N", 1, -norm1, "set");
}
removeprim(0,0,1);
}

So that wrangle in the foreach block takes a while to cook… like 9 min.

Anyway, moving on. I wrote out the resulting single segment polylines to disk.
In an empty scene, I used the cached polylines as guides for a Guide Groom and then tick marked Render Guides on the Hair Generate. It worked.
(A thanks to paristopolus from Renderman's forum for the proper Renderman setup here).

Next, I cached out the Guide Groom.


Then, I read the cached file and copied it to 4 points. This is to test if potentially a forest could be built that way with groom data.


Then, I cached the copies.

Then, I loaded them back and referenced them inside a duplicate of the original Hair Generate as a Groom Source file.


Would you say that's a viable method? Would it work for a whole forest? Is it efficient?

So far time-to-first-pixel is very fast. Each tree has upwards of 1.2 million needles.
Edited by Haki - Oct. 3, 2019 14:10:16

Attachments:
pine_needles_hair_system.jpg (457.2 KB)
pine_needles_hair_system_1.jpg (887.1 KB)
pine_needles_hair_system_2.jpg (1.2 MB)
pine_needles_hair_system_3.jpg (800.7 KB)
pine_needles_hair_system_4.jpg (593.0 KB)
pine_needles_hair_system_5.jpg (684.3 KB)
houdini_RM_instanced_groom_hair_gen_2.jpg (758.8 KB)

3D Generalist
https://hristo.one [hristo.one]
User Avatar
Member
6 posts
Joined: Feb. 2017
Offline
Is your tree coming from SpeedTree by any chance?
If yes, then you can export the geo as Alembic, and instead of needle meshes just have reference transforms exported. Then in houdini you can just load the Xforms with Alembic SOP,extract alembic fulltransform intrinsic and convert that to orient attribute. So all you`re left with are points(locations) with @orient. From there, sky is the limit..
User Avatar
Member
5 posts
Joined: Jan. 2016
Offline
Thanks, psanitra! That would be ideal, indeed, to have points with @orient almost right off the bat.

My tree is coming from Blender, however. The options there are polygon squares or circles for leaves. I went with squares and the basic idea I had was:

-for each leave
-pick either the even or the odd (doesn't matter) numbered points with %
-because each leave has 4 verts, all in the same winding order, the result is consistent
-connect them through a polyline
-construct a normal vector from 0 to 1 for 0 and inverted for 1
3D Generalist
https://hristo.one [hristo.one]
User Avatar
Member
73 posts
Joined: July 2015
Offline
What would be better is to only convert one leaf and instance that leaf on one bottom point of all others. Maybe even extract the area attribute and convert to pscale before.
Manuel Köster - FX @ Animal Logic

shadesoforange.de
User Avatar
Member
5 posts
Joined: Jan. 2016
Offline
@shadesoforange Thank you for the tip!!

Results: From ~9 min foreach block cook, down to ~3 sec single wrangle! I'm stoked!

vector pos0, pos1, norm0, norm1;
int prims[] = array(@primnum);
float perimeter = fit01(primintrinsic(0, "measuredperimeter", @primnum),0,1);

foreach (int leave; prims)
{
if (leave = @primnum)
{
int verts[] = primvertices(0, leave);
foreach (int vert; verts)
{
if (vert%4==0)
{
addprim(geoself(), "polyline", vert, vert+1);
pos0 = point(0, "P", vert);
pos1 = point(0, "P", vert+1);

norm0 = pos1 - pos0;
norm1 = pos0 - pos1;
setpointattrib(0, "N", vert, normalize(norm0), "set");
setpointattrib(0, "N", vert+1, normalize(-norm1), "set");
}
}
}
removeprim(0,leave,1);
setpointattrib(0, "pscale", @ptnum, perimeter, "set");
}


As you suggested, I instanced a polygon needle to all root points. pscale works! I don't have an orient attribute in there yet, though. For some reason, however, time-to-first-pixel in Renderman is now much much (100x) higher than the Hair System alternative. Nevertheless, I'm very happy with this progress. Thanks again!

Edited by Haki - Oct. 3, 2019 14:28:38

Attachments:
process_leaves.jpg (749.3 KB)
remove_first_point.jpg (766.3 KB)
instanced_needles.jpg (712.0 KB)

3D Generalist
https://hristo.one [hristo.one]
User Avatar
Member
73 posts
Joined: July 2015
Offline
Awesome, I can recommend just instancing a curve instead of a poly. Renderman can probably pick that up, too? RM probably also has different options to interpret curves as cards, tubes, hairs, pills..
Manuel Köster - FX @ Animal Logic

shadesoforange.de
User Avatar
Member
7 posts
Joined: Dec. 2013
Offline
I would have gone with curves, did some testing last year and its easier to get clean results with curves. We deconstructed the tree even more, and instanced clusters of needles instead of singles.
//Cards


//Curves instanced as clusters
Edited by ThomasRunning - Oct. 10, 2019 05:27:00

Attachments:
TKL_branchCluster_01_cards.png (1.1 MB)
TKL_branchCluster_01_noVariation.png (1.2 MB)

User Avatar
Member
5 posts
Joined: Jan. 2016
Offline
Thank you for the suggestions.

shadesoforange
Awesome, I can recommend just instancing a curve instead of a poly. Renderman can probably pick that up, too? RM probably also has different options to interpret curves as cards, tubes, hairs, pills..

There are only two options. The default is ribbons and the alternative is tubes and they have few curve interpolation methods.

In RenderMan the width is controlled by the width attribute. (Although now that I think about it I assigned it to points and I may have had to assign it verts).

So, here's how I tried instancing curves:

1. Procedural curve


2. Prep points to instance to


3. Copy curve to points
My intention was to use the copy to points SOP to create 1 tree first and then the instancer for making the forest.


4. Pack instanced curves (…and transfer width attribute. Although, that didn't seem to make a difference.)


5. Instance the packed prims and… it renders but doesn't take into account the width attribute for some reason. So, it looks terribly wrong and renders extremely slowly. (or often just hangs and I have to restart Houdini, especially when the curve is also packed before the copy to points.)

I also tried something new with the Hair Generate. I thought let's take the generated curves totally outside the Hair Generate and see if that can be fed directly into an instance node and most importantly, rendered correctly.

1. I cached the HAIR to disk. (the width attribute is present on the points here, too)


2. Read the cache and packed it. Instance to 25 points.


3. Renders lightning-fast and the width comes through correctly.


I'm a little bit of a newb when it comes to instancing and packed primitives. In renderman, I managed to get the width attribute working through the instance node (but not through the copy to points). Sad part there was that with every single IPR I had to restart Houdini.
Edited by Haki - Oct. 11, 2019 12:44:23

Attachments:
pine_needle_curve.jpg (403.9 KB)
points_for_instancer.jpg (681.5 KB)
copy_curve_to_points.jpg (988.3 KB)
pack_instanced_curves.jpg (494.5 KB)
cache_hairs.jpg (798.8 KB)
render_cached_hairs.jpg (777.0 KB)
instance_cached_hairs_1.jpg (727.6 KB)

3D Generalist
https://hristo.one [hristo.one]
User Avatar
Member
5 posts
Joined: Jan. 2016
Offline
Finally, finally a EUREKA moment after 5 hours of experimentation today!

In RenderMan, ‘width’ and ‘pscale’ attributes cannot co-exist.
3D Generalist
https://hristo.one [hristo.one]
User Avatar
Member
73 posts
Joined: July 2015
Offline
Ouch, but you're coming along nicely!
Manuel Köster - FX @ Animal Logic

shadesoforange.de
  • Quick Links