Hair Interpolation in Houdini with VEX

When I started learning vex programming, one of my goals was to create a hair interpolation node in Houdini. It’s one of the most complex problems I’ve solved in the area of CG grooming. It took quite some time and a lot of trial and error. But I guess where there’s a will there’s a way. I managed to put it together in a decently optimized way. This setup interpolates existing groom curves with guide curves. Hair curves and guide curves need to be resampled to the same amount.

Download Sample File

The principle of the setup:

  • Get an array of the three closest guides to each hair

  • get an array of distances to the closest guides and remap the values to be the sum of 1 for blending between the nearest guide’s point positions

  • read each closest guide’s point positions and blend between them by the distance values

  • set groom curves point positions to the blended point positions.

Remove all except root point:

//point wrangle
int pts[] = primpoints(0,@primnum);
if ( @ptnum != pts[0] ) removepoint(0,@ptnum);

Measure distance to guides:

//primitive wrangle
int pts_groom[] = primpoints(0,@primnum);
vector rootP = point(0,"P",pts_groom[0]);

int primnum_guides[] = nearpoints(1,v@P,100,3);

vector pos;
float d;

float dist[];

for ( int i=0; i<3; i++ ){
    pos = point(1,"P",primnum_guides[i]);
    d = distance(rootP,pos);
    append(dist,d);
}

float x = 1/(sum(dist));

for ( int i=0; i<3; i++ ){
    dist[i] *= x;
}

f[]@dist = dist;
f@max = max(dist);
i[]@primnum_guides = primnum_guides;

Remap dist falloff:

//primitive wrangle
float dist[] = f[]@dist;
float max = detail(0,"max");

for ( int i=0; i<len(dist); i++ ){
    dist[i] = fit(dist[i],0,max,0,1);
    dist[i] = 1-chramp("ramp",dist[i]);
}

f[]@dist = dist;

Average and set point positions:

//primitive wrangle
int primnum_guides[] = i[]@primnum_guides;
int pts[] = primpoints(0,@primnum);
vector rootP_groom = point(0,"P",pts[0]);

float dist[] = f[]@dist;

int guide1_pts[] = primpoints(1,primnum_guides[0]);
int guide2_pts[] = primpoints(1,primnum_guides[1]);
int guide3_pts[] = primpoints(1,primnum_guides[2]);

vector rootP_guide_avg,avg_pos;
vector pos1,pos2,pos3;
vector move;

int guide,guide_pts[];


for ( int i=0; i<len(pts); i++ ){
    guide = primnum_guides[i];
    guide_pts = primpoints(1,guide);
    
    if ( i==0 ){
        pos1 = point(1,"P",guide1_pts[i]);
        pos2 = point(1,"P",guide2_pts[i]);
        pos3 = point(1,"P",guide3_pts[i]);
        
        rootP_guide_avg = ((pos1*dist[0]) + (pos2*dist[1]) + (pos3*dist[2])) / (sum(dist));
    }

    pos1 = point(1,"P",guide1_pts[i]);
    pos2 = point(1,"P",guide2_pts[i]);
    pos3 = point(1,"P",guide3_pts[i]);
    
    avg_pos = ((pos1*dist[0]) + (pos2*dist[1]) + (pos3*dist[2])) / (sum(dist));
    
    move = rootP_groom - rootP_guide_avg;
    
    avg_pos += move;
    setpointattrib(0,"P",pts[i],avg_pos);
    
}
Previous
Previous

Boolean Curves With Geometry in Houdini

Next
Next

Calculate hair point distance to other hairs (VEX)