VEX language reference
Details of VEX syntax, data types, and so on.
Contexts
VEX programs are written for a specific context. For example, a shader that controls the surface color of an object is written for the surface context. A shader that determines the illuminance from a light is written for the light context. A VEX program that creates or filters channel data is written for the chop context.
The context affects which functions, statements, and global variables are available.
See VEX contexts for an overview of the ways in which you can use VEX.
If you are writing for a shading context (surface, displacement, light, etc.), you should also read the shading context specific information.
Statements
VEX supports the usual statements familiar from C. It also supports shading-specific statements such as the illuminance and gather loops that are only available in certain contexts.
Built-in functions
VEX contains a large library of built-in functions. Some functions are only available in certain contexts.
See VEX functions.
User-defined functions
Functions are defined similarly to C: specify the return type, the function name, and parenthesized list of arguments, followed by the code block.
-
Each argument can be given a default value using
=. -
Arguments of the same type can be declared in a comma separated list without re-declaring the type. Other arguments must be separated by a semi-colon.
int test(int a, b; string c) { if (a > b) { printf(c); } }
Notes
-
User functions must be declared before they are referenced.
-
The functions are in-lined automatically by the compiler, so recursion will not work.
-
As in RenderMan Shading Language, parameters to user functions are always passed by reference, so modifications in a user function affect the variable the function was called with.
-
There is no limit on the number of user functions.
-
You can have more than one return statement in a function.
-
You can access global variables directly (unlike RenderMan Shading Language, you do not need to declare them with
extern). However, we recommend you avoid accessing global variables, since this limits your function to only work in one context (where those globals exist). Instead, pass the global(s) to the function as parameters.
Main (context) function
A VEX program must contain one function whose return type is the name of the context. This is the main function of the program that is called by mantra. The compiler expects one context function per file.
This function should do the work (by calling out to built-in and/or user-defined functions) of calculating any required information and modifying global variables. You do not use the return statement to return a value from the context function. See the specific context pages for the global variables available in each context.
The arguments to the context function, if any, become the user interface for the program, for example the parameters of a shading node that references the VEX program.
If a geometry attribute exists with the same name as a parameter of the context function, the attribute overrides the parameter’s value. This lets you paint attributes onto geometry to control VEX code.
surface noise_surf(vector clr = {1,1,1}; float frequency = 1; export vector nml = {0,0,0}) { Cf = clr * (float(noise(frequency * P)) + 0.5) * diffuse(normalize(N)); nml = normalize(N)*0.5 + 0.5; }
Parameters to context functions are dealt with in a special way with VEX. It is possible to override a parameter’s value using a geometry attribute with the same name as the variable. Aside from this special case, parameters should be considered “const” within the scope of the shader. This means that it is illegal to modify a parameter value. The compiler will generate errors if this occurs.
User interface pragmas
The user interface generated from this program by Houdini will be minimal, basically just the variable name and a generic text field based on the datatype. For example, you might want to specify that frequency should be a slider with a certain range, and that clr should be treated as a color (giving it a color picker UI). You can do this with user interface compiler pragmas.
#pragma opname noise_surf #pragma oplabel "Noisy Surface" #pragma label clr "Color" #pragma label frequency "Frequency" #pragma hint clr color #pragma range frequency 0.1 10 surface noise_surf(vector clr = {1,1,1}; float frequency = 1; export vector nml = {0,0,0}) { Cf = clr * (float(noise(frequency * P)) + 0.5) * diffuse(normalize(N)); nml = normalize(N)*0.5 + 0.5; }
Operators
VEX has the standard C operators with C precedence, with the following differences.
Multiplication is defined between two vectors or points. The multiplication performs an element by element multiplication (rather than a dot or cross product; see cross and dot).
Many operators are defined for non-scalar data types (i.e. a vector multiplied by a matrix will transform the vector by the matrix). See type resolutions .
Dot operator
You can use the dot operator (.) to reference individual components of a vector or vector4:
-
.xor.rto reference the first element. -
.yor.gto reference the second element. -
.zor.bto reference the third element. -
.wor.ato reference the fourth element of a vector 4.
The dot operator is only defined for vector and vector4. The choice of the letters x,y,z/r,g,b is arbitrary; the same letters apply even if the vector doesn’t hold a point or color.
There is currently no mechanism for extracting components from matrix types. However, it is possible to get some of the information by multiplying a vector by the matrix and extracting information from the vector. See the matrix functions.
Comparisons
The comparison operators (==, !=, <, <=, >, >=) are defined when the left hand of the operator is the same type as the right hand side, for string, float and integer types only. The operations result in integer types.
The logical (&&, ||, and !) and bitwise (& |, ^, and ~) operators are only defined for integers.
Precedence table
Operators higher in the table have higher precedence.
| Order | Operator | Associativity | Description |
|---|---|---|---|
| 15 | () | L to R | Function call, expression grouping, structure member. |
| 13 | ! |
Logical negation | |
~ | One’s complement | ||
+ | Unary plus | ||
- | Unary minus | ||
++ | Increment | ||
-- | Decrement | ||
(typename) | Type cast | ||
| 12 | * | L to R | Multiplication |
/ | Division | ||
% | Modulus | ||
| 11 | + | L to R | Addition |
- | Subtraction | ||
| 10 | < | L to R | Less than |
> | Greater than | ||
<= | Less than or equal | ||
>= | Greater than or equal | ||
| 9 | == | L to R | Equal |
!= | Not equal | ||
| 8 | & | L to R | Bitwise AND |
| 7 | ^ | L to R | Bitwise exclusive OR |
| 6 | | | L to R | Bitwise OR |
| 5 | && | L to R | Logical AND |
| 4 | || | L to R | Logical OR |
| 3 | ? : | L to R | Ternary conditional |
| 2 | = | R to L | Assign to variable |
+= | Add and assign | ||
-= | Subract and assign | ||
*= | Multiply and assign | ||
/= | Divide and assign | ||
%= | Take modulus and assign | ||
&= | Take bitwise AND and assign | ||
|= | Take bitwise OR and assign | ||
^= | Take bitwise exclusive OR and assign | ||
| 1 | , | L to R | Argument separator |
Data types
VEX supports a fixed set of data types and does not allow user data types to be defined. VEX does not have an array datatype.
| Type | Definition | Example |
|---|---|---|
|
| Integer values | 21, -3, 0×31 |
|
| Floating point scalar values | 21.3, -3.2, 1.0 |
|
| Three floating point values. These values can be used to
represent positions, directions, normals or colors (RGB or HSV) |
|
|
| Four floating point values. These values can be used to
represent positions in homogeneous coordinates, or color with alpha (RGBA) |
|
|
|
A list of values. See arrays for more information. |
|
|
| Sixteen floating point values representing a 3D
transformation matrix. |
|
|
| Nine floating point values representing a 3D rotation
matrix or a 2D transformation matrix. |
|
|
| A string of characters. |
“hello world” “Mandril.pic” |
|
|
A bidirectional scattering distribution function. See writing PBR shaders for information on BSDFs. |
Type casting
Variable casting
This is similar to type casting in C++ or Java: you transform a value of one type into another (for example, an int into a float).
This is sometimes necessary, as when you have the following:
int a, b; float c; c = a / b;
In this example, the compiler will do integer division (see type resolution ). If you wanted to do floating point division instead, you need to explicitly cast a and b as floats:
int a, b; float c; c = (float)a / (float)b;
This generates additional instructions to perform the casts. This may be an issue in performance-sensitive sections of your code.
Function casting
VEX dispatches functions based not only on the types of the arguments (like C++ or Java), but also on the return type. To disambiguate calls to functions with the same argument types but different return types, you can cast the function.
For example, the noise function can take different parameter types, but can also return different types: noise can return either a float or vector.
In the code:
float n; n = noise(noise(P));
…VEX could dispatch to either float noise(vector) or vector noise(vector).
To cast a function call, surround it with typename( ... ), as in:
n = noise( vector( noise(P) ) );
(While this looks like a function call, it does nothing but disambiguate the function call inside.)
If you don’t explicitly cast the function call, VEX will print out a warning and then guess which function you wanted.
A good rule of thumb is to use function casting in preference to variable casting whenever possible to avoid performance hits.
Type resolutions
This table lists how Vex decides the type resulting from operator combinations.
| Left side | Operator | Right side | Result |
|---|---|---|---|
| matrix | + | matrix | matrix |
| matrix3 | + | matrix3 | matrix3 |
| vector4 | + | vector4 | vector4 |
| vector | + | vector4 | vector4 |
| float | + | vector4 | vector4 |
| int | + | vector4 | vector4 |
| vector4 | + | vector | vector |
| vector | + | vector | vector |
| float | + | vector | vector |
| int | + | vector | vector |
| float | + | float | float |
| int | + | float | float |
| float | + | int | int |
| int | + | int | int |
| matrix | * | matrix | matrix |
| matrix3 | * | matrix3 | matrix3 |
| matrix | * | vector4 | vector4 |
| vector4 | * | vector4 | vector4 |
| vector | * | vector4 | vector4 |
| float | * | vector4 | vector4 |
| int | * | vector4 | vector4 |
| matrix | * | vector | vector |
| matrix3 | * | vector | vector |
| vector4 | * | vector | vector |
| vector | * | vector | vector |
| float | * | vector | vector |
| int | * | vector | vector |
| float | * | float | float |
| int | * | float | float |
| float | * | int | int |
| int | * | int | int |
| float | / | vector4 | vector4 |
| int | / | vector4 | vector4 |
| float | / | vector | vector |
| int | / | vector | vector |
| float | / | float | float |
| int | / | float | float |
| float | / | int | int |
| int | / | int | int |
| float | % | vector4 | vector4 |
| float | % | vector | vector |
| float | % | float | float |
| int | % | int | int |
| ^ | vector4 | vector4 | |
| ^ | vector4 | vector4 | |
| ^ | float | float | |
| ^ | int | int |
Comments
VEX uses C++ style comments:
-
One-line comments are preceded by
// -
Freeform comments begin with
/*and end with*/
Reserved Keywords
break, bsdf, char, color, continue, do, float, for, gather, hpoint, if, int, integer, matrix, normal, point, return, string, struct, typedef, union, vector, vector2, vector4, while