OpenVDB  8.1.1
AX Code Examples

AX Code Examples

This page demonstrates a range of examples which use AX to manipulate OpenVDB point and volume data and can be used as a quick start demonstration on the capabilities of the software. These examples are constantly being updated but do not cover all aspects of AX!


Points Examples

These examples demonstrate how to use AX on OpenVDB points grids.

Basic point attributes

Below is a small example which demonstrates working with a few point attributes. The @ symbol is the identifier for an AX attribute. The type of each attribute is specified before the @ symbol and the name is specified afterwards. For example: int@count would imply an integer attribute called count.
In this example there are three point attributes: a float attribute speed, a vec3f float attribute velocity and a vec3f float attribute colour.
This snippet uses the functions length and fit to give points a colour between black and white based on their speed.
// This statement writes to a new float attribute called speed and reads from a
// vector float attribute called velocity. The length function is used here to
// return the length of the point's velocity vector.
float@speed = length(vec3f@velocity);
// Points that move faster than this threshold will all be mapped to the same
// colour (white).
float fast_threshold = 6.f;
// Writing a single value to a vector will mean that all components of the
// vector are assigned the same value.
vec3f@colour = fit(float@speed, 0, fast_threshold, 0, 1);
The velocity attribute in this example is assumed to exist and have a range of values to produce some visual variance in colour. The attributes speed and colour do not need to exist before this snippet is run as the attributes are only written to and will be created on the fly.
In Houdini (AX SOP) changing the name of the attribute from colour to Cd will result in the points being given a colour in the viewport as Cd is a special attribute.

Deleting Points

This example demonstrates a simple way of removing points from a VDB points grid. It uses a combination of axrand and axdeletepoint to randomly delete roughly half of the points in the input VDB points grid.
float threshold = 0.5f;
// The rand function takes a seed and produces a random value between 0 and 1.
// The integer attribute id from the points is used to seed the rand function.
// If the value from rand is greater than the threshold (0.5) then delete the point.
if (rand(int64@id) > threshold) {
The id attribute in this example is assumed to be a unique integer for each point in the VDB points grid such that axrand will receive a different seed value per point producing a good distribution of random values.

Applying Drag

This example shows something a bit more complex: modifying a point's velocity with a drag force and then moving the point with the modified velocity.
float dt = 1.0f / (4.0f * 24.0f); // timestep
vec3f gravity = {0.0f, -9.81f, 0.0f}; // gravity
vec3f dV = {2.0f, 0.0f, 0.0f} - v@v; // drag
float lengthV = length(dV);
float Re = lengthV * @rad / 1.225f;
float C = 0.0f;
if (Re > 1000.0f) C = 24.0f / Re;
else C = 0.424f;
// calculate drag force
vec3f drag = 0.5f * 1.2f * C * lengthV * dV * 4.0f * 3.14f * pow(@rad, 2.0f);
// update velocity
v@v += (gravity - drag / ((4.0f / 3.0f) * 3.14f * pow(@rad, 3.0f))) * dt;
// update position
v@P += v@v * dt;
The final AX command, v@P += v@v * dt; writes to the position attribute of the points v@P. Whenever position is written to in this way, the AX PointExecutable will move (re-bucket) points in PointDataGrids.

Curl Noise

The following example demonstrates using various native AX functions to calculate a position based curl noise on points in a particular group, and to use that calculation to update point velocities.
// Only calcualte noise and apply it to points if the current point is NOT in
// a group called "escaped". Note that the PointExecutable also has native
// support for group based execution.
if (!ingroup("escaped")) {
// Read custom data
float amplitude = $amplitude;
float persistence = $persistence;
float lacunarity = $lacunarity;
vec3f freq = vec3f$frequency;
vec3f offset = vec3f$offset;
vec3f noise = 0.0f;
// Position based curl simplex noise
for (int octave = 0; octave < int($octaves); ++octave) {
vec3f noisePos = vec3f@P * freq + offset;
noise += curlsimplexnoise(noisePos) * amplitude;
amplitude *= persistence;
freq *= lacunarity;
// Apply noise scaled by the current velocity length
vec3f@v += length(vec3f@v) * noise;


AX supports 3x3 and 4x4 matrix types and various transformations. They can be accessed via row, column operators or as a flat array and have defined operators (such as matrix multiplication etc.). The following demonstrates some trivial matrix math on VDB Point positions.
// get the 4x4 identity matrix. note that scalar->matrix promotion handles
// this, so writing 'mat4f transform = 1;' has the same effect.
mat4f transform = identity4();
// set the X transform component
transform[3,0] = 5;
// pre scale the matrix. this is equal to writing: 'transform = scale * transform;'
vec3f scale = { 1,2,3 };
prescale(transform, scale);
// double the value of the scale Y component
transform[5] *= 2;
// construct a basic rotation matrix, representing 90 degree rotation around Y
float degrees = 90.0f;
float rad = radians(degrees);
mat3f rotation = {
cos(rad), 0.0f, -sin(rad),
0.0f, 1.0f, 0.0f,
sin(rad), 0.0f, cos(rad)
// apply the rotation and transformation to the current points position
vec3f@P *= rotation;
vec3f@P *= transform;

Volume Examples

These examples demonstrate how to use AX on VDB volume grids.

Volume Clamping

Below is a small example of reading adn writing to voxels within a VDB volume. As with points, the @ symbol specifies an access to a named volume, with the type of each volume preceding it and the name appearing as a suffix. For example: float@density would imply a float volume called density.
This snippet uses axclamp function to constrain the density attribute between zero and one.
float@density = clamp(float@density, 0.f, 1.f);

Velocity Update

AX can be used to read and write to multiple volumes. Here we have a typical example of a eulerian velocity update which takes into account an updated vector force and scalar mass.
// Access the current timestep - this could instead be provided by the AX
// integration and made accessible with custom data (i/e: float$timestep)
float dt = 1.0f / 24.0f;
// read from the mass grid the corresponding (matching index space) value
float mass = float@mass;
// safe divide, in case mass is zero
float massInverse = 0.0f;
if (mass > 0.0f) massInverse = 1.0f / mass;
// update the current velocity value by scaling the corresponding force
// by the timestep and inverse mass
vec3f@vel += dt * massInverse * vec3f@force;

Linear Blending

Here we demonstrate how AX can be used to blend two volumes together. Note that only the volumes which are written to are executed over. In the below code, only @surface_a is written to. This means that the final blended result, stored in surface_a, will only be updated in surface_a's topology. Should we want a combined blend of non overlapping areas of both surface a and b, we would need to activate a's topology with respect to b first.
// time constants for each run.
float time = float$time;
float maxDuration = 100.0f;
// read both surface a and surface b values
float a = @surface_a; // source value
float b = @surface_b; // target value
// calculate the fractional mask value in respect to time
float mask = time / maxDuration;
// optionally scale the mask by a per index space density. if
// $enable_density_mask is true, a density grid is used to vary the blend per
// voxel
if ($enable_density_mask) mask *= @density;
// finally, clamp the mask value and update the level set value in surface a.
// Note that this may produce a no longer valid level set and might need to be
// rebuilt!
mask = clamp(mask, 0.0f, 1.0f);
@surface_a = a + (b-a) * mask;