Practical VEX Techniques for Geometry Manipulation and Particle Control in Houdini

Bounding Box Based Rotation

vector bbox_low, bbox_high;
getbbox(bbox_low, bbox_high);
float spin_limit = 4;
matrix3 base_mtx = ident();
float spin_factor = fit(@P.y, bbox_low.y, bbox_high.y, 0, 1);
rotate(base_mtx, spin_factor * spin_limit, {0, 1, 0});
@P *= base_mtx;

Custom Parameter UI Definition

#pragma label oper_mode "Operation"
#pragma label shift_amt "Offset Value"
#pragma group XFORM shift_amt oper_mode

#pragma choice oper_mode "0" "Bend"
#pragma choice oper_mode "1" "Twist"
#pragma choice oper_mode "2" "Taper"

Neighbor Velocity Averaging for Acceleration

float search_rad = 99999;
int max_hits = 10;
int src = pcopen(@OpInput1, "P", @P, search_rad, max_hits + 1);

int pt_idx;
int found_cnt = 0;
vector neighbor_vel;
vector vel_sum = {0, 0, 0};

while (pciterate(src)) {
    pcimport(src, "point.number", pt_idx);
    if (pt_idx == @ptnum)
        continue;

    pcimport(src, "v", neighbor_vel);
    vel_sum += neighbor_vel;
    found_cnt++;
}

if (found_cnt > 0) {
    vel_sum /= float(found_cnt);
    v@accel += (vel_sum - @v) * 0.4;
}

Orienting Copied Instances from Velocity

v@N = normalize(@v);
v@up_vec = {0, 1, 0};
v@side_vec = normalize(cross(v@N, v@up_vec));
v@up_vec = normalize(cross(v@N, v@side_vec));

Determining Planar Angle and Relatvie Direction

// Returns angle between vectors a and b in range [0, PI]
float vec_angle(vector a; vector b) {
    return acos(dot(normalize(a), normalize(b)));
}

// For XY projection: returns -1 if b is left of a, 1 if right (view from -Z)
int side_flag(vector a; vector b) {
    vector z_comp = normalize(cross(normalize(a), normalize(b)));
    return (z_comp.z < 0) ? 1 : -1;
}

vector base_a = point(0, "P", 1) - point(0, "P", 0);
vector base_b = point(1, "P", 1) - point(1, "P", 0);
float angle_val = vec_angle(base_a, base_b);
int orientation = side_flag(base_a, base_b);

if (orientation == -1) {
    printf("direction: left\n");
    printf("degrees  : %d\n", int(degrees(angle_val)));
} else {
    printf("direction: right\n");
    printf("degrees  : %d\n", int(degrees(angle_val)));
}

Fractal Turbulence Noise Generator

float fractal_noise(vector pos; vector freq; vector offs;
                   float rough; float lac; int steps; float expo) {
    float total = 0;
    float amp = 1.0;
    vector sample_pos = pos * freq + offs;

    for (int o = 0; o <= steps; o++) {
        total += pow(noise(sample_pos), expo) * amp * 4;
        sample_pos *= lac;
        amp *= rough;
    }
    return total;
}

vector freq_vec = chv("freq");
vector off_vec = chv("offset");
float rough_f = chf("rough");
float lac_f = chf("lacunarity");
int oct_num = chi("octaves");
float exp_f = chf("exp_scale");
float scl = chf("global_scale");

float nval = fractal_noise(@P, freq_vec, off_vec, rough_f, lac_f, oct_num, exp_f);
@P.y += nval;

Matching Points Against Multiple Groups

int match_res = 0;
string grp_list[] = split(chs("group_mask"));
foreach (string g; grp_list) {
    match_res = inpointgroup(0, g, @ptnum);
}

Accessing Global Frame Count

float frame_num = atof(`$NFRAMES`);
printf("%g\n", frame_num);

Appending Points to Exisitng Geometry

int last_idx = npoints(geoself()) - 1;
vector new_pos = point(geoself(), "P", last_idx) + set(0, 0, 0.1);
int new_pt = addpoint(geoself(), new_pos);
addvertex(geoself(), 0, new_pt);

Steering Partciles Along a Curve

float close_r = ch("close_range");
float far_r = ch("far_range");
int curve_src = pcopen(1, "P", @P, 10000, 30);

int cur_idx = i@target_idx;
int prev_idx = i@target_idx;
float path_len = 0;
vector align_dir, guide_dir;
int hit_cnt = 0;

if (pcnumfound(curve_src) > 0) {
    while (pciterate(curve_src)) {
        int seg_idx, src_id;
        pcimport(curve_src, "curve_id", seg_idx);
        pcimport(curve_src, "source_curve", src_id);
        pcimport(curve_src, "N", guide_dir);
        pcimport(curve_src, "P", align_dir);
        pcimport(curve_src, "sum", hit_cnt);
        pcimport(curve_src, "perimeter", path_len);

        if (seg_idx > cur_idx && src_id == i@src_curve) {
            if (seg_idx - cur_idx <= 3) {
                i@target_idx = seg_idx;
                break;
            }
        }
    }
}

if (i@target_idx <= prev_idx) {
    i@target_idx += 1;
    int pt_cnt = findattribvalcount(1, "point", "curve_id", i@target_idx);
    for (int k = 0; k < pt_cnt; k++) {
        int tmp_pt = findattribval(1, "point", "curve_id", i@target_idx, k);
        int src = point(1, "source_curve", tmp_pt);
        if (src == i@src_curve) {
            guide_dir = point(1, "P", tmp_pt);
            align_dir = point(1, "N", tmp_pt);
            break;
        }
    }
}

v@guide_pos = guide_dir;
vector to_target = (guide_dir - @P) * (float(cur_idx) / hit_cnt);
float ratio = float(cur_idx) / hit_cnt;
vector force_a = normalize(to_target) * chf("power") * (frand(@id * 6.7) * 0.6 + 0.4);
vector force_b = normalize(align_dir) * chf("power") * (frand(@id * 6.7) * 0.6 + 0.4) * (ratio * 0.5 + 0.8);
v@steer_force = lerp(force_a * 14, force_b * 70, fit(distance(guide_dir, @P), 0.08, 0, 0, 1));
v@steer_force *= (frand(@id * 104.6) + 0.01);

Tags: Houdini VEX Geometry Deformation Particle Systems Procedural Modeling

Posted on Wed, 20 May 2026 02:50:15 +0000 by kanika