VEX

Snippets, helpers, and workflow notes for Houdini VEX.

Detail Attribute From Relative Node Path

Access a detail attribute from another node without consuming an input.

ContextAttribute Fetch Use caseCross-node Reference Run overGeneric
// Acessing an attribute from a node relative to this network
// Useful if you run out of inputs
float scale = detail("op:" + opfullpath("../NODE"), "scale_amount", 0);

Normal Dot Ramp Mask

Build a ramp-driven mask from normal direction for top-surface scattering control.

ContextNormal Mask Use caseScatter Control Run overPoints
// create a color from a vector and a dot product with a ramp
// useful for scattering points on the top surface of something
vector up = {0,1,0};
float dot = dot(@N, up);
v@Cd = dot * chramp("dotramp", dot);
Normal dot ramp visualization in Houdini

Volume Density Cull

Cull points using sampled density from a secondary volume/SDF input with a controllable threshold.

ContextSDF Density Cull Use casePoint Removal Run overPoints
//remove points based of SDF denisty input of second wrangle input
// a float slider can control the falloff
float sample = volumesample(1, "density", @P);
if (sample < chf("remove")) removepoint(0,@ptnum);

Set Packed Intrinsic Transform

Rotate packed geometry using intrinsic transform with randomized per-point variation.

ContextPacked Intrinsic Transform Use casePacked Rotation Run overPrimitives
// Set intrinsic transform on packed geo
matrix3 xform = primintrinsic(0, "transform", @primnum);
float amount = chf("rotate");
amount -= rand(@ptnum);
vector axis = normalize({-1,0,0} * xform);

rotate(xform, amount, axis);
setprimintrinsic(0, "transform", @primnum, xform, "set");

Cd Levels Control

Apply levels-style controls to `@Cd` using black/white points and gamma.

ContextCd Levels Control Use caseColor Remap Run overPrimitives
//levels style controls for the @Cd attribute
float gamma = chf("Gamma");
float ShadowValue = chf("Black");
float HighlightValue = chf("White");
float OutShadowValue = chf("Out_Black");
float OutHighlightValue = chf("Out_White");
@Cd = pow(@Cd, (1 / gamma));
@Cd = ((@Cd - ShadowValue) / (HighlightValue - ShadowValue));
@Cd = (@Cd * (OutHighlightValue - OutShadowValue)) + OutShadowValue;
@Cd = fit01(@Cd, 0, 1);

HSV Adjustments

Adjust hue, saturation, and lightness on `@Cd` through HSV conversion.

ContextHSV Adjustments Use caseColor Grading Run overPrimitives
// hsv adjustments
vector hsv = rgbtohsv(@Cd);
hsv.x+=chf('hue_offset');
hsv.y+=chf('saturation');
hsv.z+=chf('lightness');
vector rgb = hsvtorgb(hsv);
@Cd = rgb;

Scale Packed Prims Over Time

Animate packed primitive scale down over time with per-primitive random variation.

ContextPacked Scale Over Time Use casePacked Transform Animation Run overPrimitives
//scale down packed prims over time with randon values
float min = chf("Min");
float max = chf("Max");
vector randscale = fit01(float(rand(@primnum)), min, max);
vector outscale = 1 - (randscale * @Frame);
vector scale = clamp(outscale, 0, 1);
matrix3 trn = primintrinsic(0, "transform", @primnum);
matrix scalem = maketransform(0, 0, {0,0,0}, {0,0,0}, scale, @P);
trn *= matrix3(scalem);
setprimintrinsic(0, "transform", @primnum, trn);

Random Tangent Rotation

Apply random point rotation along a curve using tangent/up-driven orientation.

ContextCurve Random Rotation Use caseNormal Variation Run overPoints
//random point roataion on a curve following tangent,
//requires up attribute that can be made from tangent in polyframe
vector dir= v@up;
@N=cross(dir,v@N);
matrix3 rotate= ident();
float radiansamount = fit01(rand(@ptnum),0,1000);
rotate(rotate,radians(radiansamount),dir);
@N*=rotate;
Random tangent rotation setup preview

Liquid Crown Vector Setup

Build tangent-driven normal rotation and ramp-shaped velocity for liquid crown style motion.

ContextLiquid Crown Vectors Use caseVelocity Shaping Run overPoints
//create liquid crown effects from vectors
@N = set(0,1,0);
v@tangentV = set(0,1,0);
int numpoints = npoints(0);

if (@ptnum != numpoints-1) {
    v@tangentV = normalize(point(@OpInput1, "P", @ptnum + 1) -@P);
    } else {
    v@tangentV = normalize(point(@OpInput1, "P", 0) -@P);
    }
//rotate normals around tangentV
matrix ref = ident();
float angle  = radians(ch("angle"));
rotate(ref, angle, v@tangentV);
@N = @N * ref;
//create velocity shape
float npoints = @ptnum / float (@numpt) * ch("Frequency");
float rampMult = chramp("Ramp_Mult", npoints, 0);
@N *= pow(rampMult * ch("General_Mult"), ch("Exponent"));
@v = @N;
Liquid crown vector setup preview

Random 180 Rotations

Add randomized 180 degree orientation offsets to points using quaternions.

ContextRandom 180 Rotation Use caseOrientation Variation Run overPoints
//add random 180 rotations
float randomy = fit01(rand(i@ptnum+1), (-100), 100);
float randomyint = rint(randomy);
float y = (@ptnum+randomyint)*180;
vector rotDegree = set(0,y,0);
vector4 rotQua = eulertoquaternion(radians(rotDegree ), 0);
p@orient = qmultiply(p@orient, rotQua);
Random 180 rotation result preview

Two-Part Array Transfer (Detail to Points)

Create a point array in detail mode, then read and iterate it in point mode.

ContextPoint Array Transfer Use caseIndex Mapping Run overDetail + Points
//create array from all points - run in detail
int pts[] = expandpointgroup(0,"");
i[]@pts = pts;
//run over the array in point mode
int importarray[] = detail(1, 'pts');

foreach(int i; importarray) {
    if (@ptnum-1==i) {
        @id = i;
        }
    }

VEX Attribute Transfer With Random Radius

Point-based color transfer from input 1 with randomized influence radius per source point.

ContextAttribute Transfer Use caseColor Transfer Run overPoints
//vex equivilent of attribute transfer from input 1
//inclues random radius
vector pos, col;
int pts[];
int pt;
float d;

pts = nearpoints(1,@P,40);  // search within 40 units
@Cd = 0;  // set colour to black to start with

foreach(pt; pts) {
    pos = point(1,'P',pt);
    col = point(1,'Cd',pt);
    d = distance(@P, pos);
    d = fit(d, 0, fit01(rand(pt), chf('min_rad'), chf('max_rad')), 1,0);
    d = clamp(d,0,1);
    @Cd += col*d;
}
Attribute transfer random radius preview

Align Geometry To Input Normal

Rotate geometry by aligning a reference vector to a normal sampled from input 1.

ContextNormal Align Rotation Use caseGeometry Orient Run overPoints
//rotate geometry based on normal
// Point with normal from second input to align
vector to = point(1, 'N', 0);

// Align "from" normal to the following vector
// Adjust this depending on orientation
vector from = {0,1,0};

// Calculate the rotation matrix using dihedral
matrix3 m = dihedral(normalize(from), normalize(to));

// Apply the rotation to the geometry
@P = @P * m;
@N = @N * m;

Anti-Aliased Flow Noise

Use `vop_fbmFlowNoiseVV` for stable flow noise and remap to display-friendly color.

ContextFlow Noise Use caseProcedural Noise Color Run overPoints
//anti-aliased flow noise in VEX
//more functions can be found here
// C:\Program Files\Side Effects Software\Houdini 19.0.455\houdini\vex\include\voplib.h
#include <voplib.h>
vector noise = vop_fbmFlowNoiseVV(@P * chv("freq") - chv("offset"), ch("rough"), chi("maxoctave"), ch("flow"), ch("flowrate"), ch("advect"));
noise *= ch("amp");
v@Cd = noise+0.5;

Random Threshold Point Removal

Cull points randomly using a seed and threshold control.

ContextRandom Threshold Removal Use casePoint Culling Run overPoints
//remove random points based on threshold percentage
if ( rand(@ptnum+chf('seed')) > chf('threshold') ) {
    removepoint(0,@ptnum);
    }

Noise Spots Mask

Create spot-like color masks from thresholded noise.

ContextNoise Spots Mask Use caseSurface Spotting Run overPoints
//adding spots with noise
#include <voplib.h>
float threshold = chf('threshold');
vector4 pos = set(v@P.x, v@P.y, v@P.z, 0);
vector noise = vop_fbmNoiseFP(pos*chf('frequency'), chf('roughness'), 8, 'noise')*chf('amplitude');
float float_noise = fit(noise.x, -0.5, 0.5, 0, 1);
if (float_noise<threshold) {
    float_noise = 0;
    }

if (float_noise>threshold) {
    float_noise = 1;
    }

@Cd+=float_noise*chf('mix_amount');
Noise spots mask preview

Closest Point Distance (Self)

Measure distance to the nearest other point on the same input.

ContextClosest Point Distance Use caseProximity Measure Run overPoints
//measure distance to closest point (self)
int pts[] = nearpoints(0, @P, 1, 2);
int pt = pts[1];
@dist = distance(@P, point(0, 'P', pt));
Closest point distance visualization

Class Attribute Range Fit

Remap class IDs into a texture index range for multi-ID material variation.

ContextClass Range Fit Use caseMaterial ID Randomization Run overPrimitives
//fit a class attribute to a range, this is useful for a multi-id-material
//set the multitex to random by texture and make the texture a user colour reading the class attribute
//this needs to run on primitives
int tex_count = chi('texture_count');
i@class = rint(fit01(rand(i@class), 0, tex_count-1));

VEX Ambient Occlusion

Compute a point-based ambient occlusion value by sampling hemisphere rays.

ContextAmbient Occlusion Use caseOcclusion Mask Run overPoints
// VEX Ambient Occlusion
// Assign initial variables, including P with small surface offset.
vector pos = @P + (@N * pow(10, -6));
int samples = 256; float radius = chf('radius'); float ao;

for(int i = 0; i < samples; i++)
{
    // For each sample, create a random hemispherical direction using N.
    vector2 seed = rand(@ptnum + i);
    vector dir = sample_hemisphere(@N, seed);

    // Export position of directional intersection, limited to radius.
    vector ipos; vector iuvw;
    float isect = intersect(0, pos, dir * radius, ipos, iuvw);

    // If intersection is found, fit ray length into a 1-0 range and
    // add the result to the accumulating variable 'ao'.
    if(isect != -1)
    {
        ao += fit(distance(ipos, pos), 0, radius, 1, 0);
    }
}
// When all samples are iterated over, divide the total ao sum by
// total number of samples, then returning its complement.
f@ao = 1 - (ao / samples);
Ambient occlusion result preview

Set Vector Attribute As Color

Mark a vector attribute as color type info for Alembic to Maya workflows.

ContextColor Type Info Use caseAlembic Maya Compatibility Run overPoints
//setting a vector to a colour type attribute
//this is imprtant for an alembic to maya workflow
v@clr = 0;
setattribtypeinfo ( 0, "point", "clr", "color" );

Euler Style XYZ Rotations

Apply sequential X/Y/Z rotations to orientation vectors via a transform matrix.

ContextEuler Rotations Use caseOrientation Control Run overPoints
// euler style x,y,z rotations in VEX
@N = {0,0,1};
v@up = {0,1,0};
v@out = cross(@up, @N);
matrix3 m = maketransform(@N, @up);

float rotate_x = radians(chf('rotate_x'));
rotate(m, rotate_x, @out);

float rotate_y = radians(chf('rotate_y'));
rotate(m, rotate_y, @up);

float rotate_z = radians(chf('rotate_z'));
rotate(m, rotate_z, @N);
@N = {0,0,1}*m; v@up = {0,1,0}*m; @out = cross(@up, @N);

Attribute Interpolate Source Arrays

Build `sourcepts` and `sourceweights` arrays for Attribute Interpolate workflows.

ContextAttribute Interpolate Arrays Use caseSource Point Weights Run overPoints
//creating arrays for use with the attribute interpolate node
int pts[]; float weights[];
push(pts, @ptnum);
float weight = point(0, "mask", @ptnum);
weight = chramp('weight_remap', weight);
push(weights, weight);
i[]@sourcepts = pts;
f[]@sourceweights = weights;

Matrix Box Scaling

Scale geometry around its bounding box center using a matrix transform.

ContextMatrix Box Scaling Use caseProcedural Deform Run overPoints
//scaling a box
// create a matrix
matrix m=ident();

float xyratio = ch('xyratio');
scale(m, set(1, xyratio, 1)); //scale matrix

vector center=getbbox_center(0);

v@P-=center; // matrix multiplication works relative to world space, so let's move our box there.
v@P*=m; // apply matrix
v@P+=center; // let's move our box back to where it was
v@Cd=chramp( "color", xyratio);

Match Points By ID Across Inputs

Find matching point IDs on input 1 and snap positions to the matched point.

ContextID Point Matching Use caseCross-Input Point Alignment Run overPoints
//match points to second input with same id attribute
if (@id!=-1) {
    int point_num = findattribval(1, "point", "id", @id);
    if(point_num!=-1) {
        vector pos = point(1, "P", point_num);
        @P = pos;
        }
    }

Polyloft In VEX

Bridge corresponding primitive edges across two inputs and build lofted quads.

ContextPolyloft Use casePrimitive Bridging Run overPrimitives
//polyloft in vex
int pts1[] = primpoints(0, @primnum);
int pts2[] = primpoints(1, @primnum);

for(int i=0; i<len(pts1)-1; i++){
    int pt1_1 = pts1[i];
    int pt1_2 = pts1[i + 1];
    
    int tpt2_1 = pts2[i];
    int tpt2_2 = pts2[i + 1];
    
    vector pos2_1 = point(1, "P", tpt2_1);
    vector pos2_2 = point(1, "P", tpt2_2);
    
    int pt2_1 = addpoint(0, pos2_1);
    int pt2_2 = addpoint(0, pos2_2);
    
    int prim = addprim(0, "poly", pt1_1, pt1_2, pt2_2, pt2_1);
}

removeprim(0, @primnum, 1);
Polyloft in VEX result preview

Frame Loop Setup For TimeShift

Loop a chosen frame range after a trigger frame and reference `@frame` in TimeShift.

ContextFrame Loop TimeShift Use caseTime Remap Looping Run overDetail
//loop over set frames after defined frame with a timeshift
//referce @frame in timeshift
int current_frame = chi('frame');
int loop_start = chi('loop_start_frame');
int loop_end = chi('loop_end_frame');
int loop_begin = chi('frame_to_begin_loop');
i@frame = current_frame;
int loop_length = loop_end-loop_start;
int offset = current_frame-loop_start;
int mod = offset%loop_length;
if (current_frame<loop_begin) {
    i@frame = current_frame;
    }
if (current_frame>=loop_begin) {
    i@frame = loop_start+mod;
    }
f@loop_length = loop_length;
f@mod = mod;

Conform To Input Surface

Project points to input 1 using forward and fallback reverse normal ray tests.

ContextGeometry Conform Intersect Use caseSurface Conform Run overPoints
// conform geo to second input
int pt; vector ipos; vector iuvw;
pt = intersect(1, @P, @N*10, ipos, iuvw);
if (pt==-1) {
    pt = intersect(1, @P, @N*-10, ipos, iuvw);
    }
@P = ipos;
Conform to surface result preview

Create Circle In Detail Wrangle

Generate circle points procedurally from sample count, radius, and origin controls.

ContextProcedural Circle Generation Use casePoint Creation Run overDetail
// Create a circle
// Run on detail mode
int sample = chi("sample");
float radius = ch("radius");
vector origin = chv("origin");
float two_pi = $PI * 2;
float theta = 0;
float step_angle = two_pi/float(sample);
float x,z;
vector pos;
while( theta < two_pi){
    x = origin.x + cos(theta) * radius;
    z = origin.z + sin(theta) * radius;
    pos = set(x, origin.y, z);
    addpoint(0, pos);
    theta += step_angle;
    }