Skip to content

Procedural Data Manipulation

Warping point mask similar to Substance designer warp operation

Very often we have to paint mask manually to keep the result art-directed. If it’s simple it’s fine, but it can be time-consuming if you wanna keep things organic and non-uniform, especially on edges of the mask. Due to this, in Substance Designer you can break the mask edges with a warp operation to achieve a non-uniform result, and fortunately for us, Houdini gives you freedom to do it as well - but not out of the box, as far as I know.

So my solution in this case is quite simple:

  1. Calculate procedural UV coordinates for primitives with the xyzdist() function
  2. Distort uvw with any noise you want
  3. Sample original mask attribute using primuv() with our procedurally distorted uvw coordinates

The short story long - to distort an already painted mask, we have to create UV coordinates and distort them with noise, which gives us a distorted UV space that we use to sample the original mask into this UV space. As a result, we get a distorted mask and re-apply it back to our geo.

It may sound quite difficult, but lets remember Photoshop’s Warp tool. The Warp tool returns a grid for the selected element, which has points - this is our procedural uvw returned by the xyzdist() function. Then we move the points of the warp tool, and our selected element gets distorted. This is exactly what we’re doing with our uvw when multiplying it by noise - we’re moving UV points to get distortion.

This is a very simplified example which helps you visualize this process in your head.

Point mask warp

VEX example

int prim;
vector uvw;
int visualise = chi("visualise");
int bypass = chi("bypass_warp");
string noise_attribute = chs("noise_attribute");
string mask_attribute = chs("mask_attribute");
float mask_rest = point(0, mask_attribute, @ptnum);
float noise_warp = point(0, noise_attribute, @ptnum);
vector N = normalize(point(0, "N", @ptnum));
if ( haspointattrib(0, noise_attribute) == 0){
noise_warp = (noise(@P * 5.0) - 0.5) * 2;
}
vector noise = noise_warp * chf("noise_multiply");
vector tangent = noise - dot(noise, N) * N;
vector warped_pos = @P + tangent * chf("position_warp");
xyzdist(0, warped_pos, prim, uvw);
float mask = primuv(0, "mask", prim, uvw);
if ( bypass == 1 ) {
mask = mask_rest;
}
setpointattrib(0, mask_attribute, @ptnum, mask);
if ( visualise == 1 ) setpointattrib(0, "Cd", @ptnum, mask);