268 lines
9.2 KiB
Plaintext
268 lines
9.2 KiB
Plaintext
/**
|
|
<div style="width:800px;text-align:justify;">
|
|
|
|
@page axexamples AX Code Examples
|
|
|
|
@section axexamplesintro AX Code Examples
|
|
@par
|
|
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!
|
|
|
|
@section axexamplecontents Contents
|
|
- @ref axexamplepoints
|
|
- @ref axexamplepointbasic
|
|
- @ref axexamplepointdelete
|
|
- @ref axexamplepointdrag
|
|
- @ref axexamplepointcurlnoise
|
|
- @ref axexamplepointtransforms
|
|
- @ref axexamplevolumes
|
|
- @ref axexamplevolumeclamp
|
|
- @ref axexamplevolumevel
|
|
- @ref axexamplevolumeblend
|
|
|
|
|
|
@section axexamplepoints Points Examples
|
|
@par
|
|
These examples demonstrate how to use AX on OpenVDB points grids.
|
|
|
|
@subsection axexamplepointbasic Basic point attributes
|
|
@par
|
|
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: @b `int\@count` would imply an @b integer attribute called @b count.
|
|
@par
|
|
In this example there are three point attributes: a @b float attribute @b speed,
|
|
a @b vec3f float attribute @b velocity and a @b vec3f float attribute @b colour.
|
|
@par
|
|
This snippet uses the functions @ref axlength "length" and @ref axfit "fit" to
|
|
give points a colour between black and white based on their speed.
|
|
@par
|
|
<!-- P -->@code{.c}
|
|
// 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);
|
|
@endcode
|
|
@par
|
|
The @b velocity attribute in this example is assumed to exist and have a range
|
|
of values to produce some visual variance in colour. The attributes @b speed and
|
|
@b 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.
|
|
@par
|
|
@note
|
|
In Houdini (AX SOP) changing the name of the attribute from @b colour to @b Cd
|
|
will result in the points being given a colour in the viewport as Cd is a
|
|
special attribute.
|
|
|
|
@subsection axexamplepointdelete Deleting Points
|
|
@par
|
|
This example demonstrates a simple way of removing points from a VDB points grid.
|
|
It uses a combination of @ref axrand and @ref axdeletepoint to randomly delete
|
|
roughly half of the points in the input VDB points grid.
|
|
@par
|
|
<!-- P -->@code{.c}
|
|
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) {
|
|
deletepoint();
|
|
}
|
|
@endcode
|
|
@par
|
|
The @b id attribute in this example is assumed to be a unique @b integer for
|
|
each point in the VDB points grid such that @ref axrand will receive a
|
|
different seed value per point producing a good distribution of random values.
|
|
|
|
@subsection axexamplepointdrag Applying Drag
|
|
@par
|
|
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.
|
|
@par
|
|
<!-- P -->@code{.c}
|
|
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;
|
|
@endcode
|
|
@par
|
|
The final AX command, `v@P += v@v * dt;` writes to the position attribute of
|
|
the points @b `v\@P`. Whenever position is written to in this way, the AX
|
|
`PointExecutable` will move (re-bucket) points in PointDataGrids.
|
|
|
|
@subsection axexamplepointcurlnoise Curl Noise
|
|
@par
|
|
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.
|
|
@par
|
|
<!-- P -->@code{.c}
|
|
// 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;
|
|
}
|
|
@endcode
|
|
|
|
@subsection axexamplepointtransforms Transformations
|
|
@par
|
|
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.
|
|
@par
|
|
<!-- P -->@code{.c}
|
|
// 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;
|
|
@endcode
|
|
|
|
|
|
<br/><hr>
|
|
|
|
|
|
@section axexamplevolumes Volume Examples
|
|
@par
|
|
These examples demonstrate how to use AX on VDB volume grids.
|
|
|
|
@subsection axexamplevolumeclamp Volume Clamping
|
|
@par
|
|
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 @b float volume called @b density.
|
|
@par
|
|
This snippet uses @ref axclamp function to constrain the @b density attribute
|
|
between zero and one.
|
|
@par
|
|
<!-- V -->@code{.c}
|
|
float@density = clamp(float@density, 0.f, 1.f);
|
|
@endcode
|
|
|
|
@subsection axexamplevolumevel Velocity Update
|
|
@par
|
|
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.
|
|
@par
|
|
<!-- V -->@code{.c}
|
|
// 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;
|
|
@endcode
|
|
|
|
@subsection axexamplevolumeblend Linear Blending
|
|
@par
|
|
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 @b `\@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 @b non @b overlapping areas of both surface a and b,
|
|
we would need to activate a's topology with respect to b first.
|
|
@par
|
|
<!-- V -->@code{.c}
|
|
// 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;
|
|
@endcode
|
|
|
|
</div>
|
|
*/
|