primuv & xyzdist
This is mostly a write up of different blog posts, tutorials, cg wiki’s Joy Of Vex 19 and Toadstorm’s The joy of xyzdist() and primuv() for myself, so I recommend to check those out first!
# Usecases
- Stick things to geometry using VEX
- Smoothly sample / interpolate attribute values from the closest geometry (avoids flickering as closest point changes -> problem with
minpos()
/nearpoint()
# primuv() function
primuv()
provides a way to smoothly interpolate an attribute between points on the target geometry.
In this example I sample the color and the position at the vector uv
and apply it to a single point before copying a sphere to it.
// pointwrangle “sample_primuv”:
|
|
// vex definition
|
|
If you give it a primid and a uv coordinate it returns the value of the specified attribute at the uv-interpolated position. UVs in this case are not typical uv coordinates used for texturing, but special parametric UVs.
# Interpolating quaternions (p@orient)
To correctly interpolate quaternions you need spherical interpolation. The primuv
function only interpolates linearly. To counter this issue you can convert any quaternion on your reference geometry to a matrix3
transform and then sample it, before converting it back to a quaternion on the target geometry.
This is basically the same as creating 3 vectors, which represent the quaternion, and interpolating those.
You should also use polardecomp()
on your matrix to make sure it is orthonormal and avoid getting “a wonky quaternion”.
the full code looks like this:
// point wrangle on reference geometry
|
|
// point wrangle on target geometry
|
|
Sources: @Reinhold pointed this out on a houdini discord. Thanks!!
# Parametric UVs vs Regular UVs
While regular UVs usually span accross multiple primitives and map a 2D space to the 3D geometry, paramteric UVs are calculated automatically per primitive. They also don’t exist as a @uv
attribute.
Parametric UVs are a 0 to 1 gradient mapped across the prim. Think of a default STMap in Nuke where the x and y-axis resolution is fit to a 0-1 range and put into the red and green image channels.
You can visualize them using the scatter SOP. Just turn on sourceprimuv
but change it to Cd
in the output
tab.
All the primitives (NOT volumes, vdbs, metaballs) have continuous parametric UVs:
# xyzdist() function
While the minpos()
function can be used to find the closest point on a geometry, the xyzdist()
function returns the id
of the closest and the parametric uv
coordinates of this primitive at which a point would be closest. This info can then be fed into the primuv()
function.
// vex definition
|
|
The &
symbols mean that vex will write to the attributes you specify. This is necessary because the xyzdist()
function can return more than one value at the same time and vex can’t handle filling multiplte variables at once otherwise. It’s similar to the rotate
and scale
matrix functions.
By default xyzdist()
returns the distance to the closest “point” that lies on the surface of the target geometry. By using the additional &attributes
we can get the id and uv information I mentioned before.
The usual setup to query information by combining it with the primuv()
function looks something like this:
|
|
Unlike minpos()
you can read all sorts of attributes with this and not just @P
.
# Make Stuff Stick to Animated Geo
To make this work we need to make sure to:
- create a
p@orient
attribute that follows the animated geo correctly - freeze the animation before placing calculating
xyzdist()
// pointwrangle “xyzdist”
|
|
//pointwrangle “primuv”
|
|
The rest ist pretty much the same as described above. Calculate xyzdist()
, use primuv()
to fetch @orient
from the animated geo, copy to points etc.
Causes Issues!
This setup works 95% well but can cause issues with not correctly interpolated orients. I described above how to work around it.
sources / further reading: