Lets you add (shader/geometry) nodes by typing in a formula into a prompt. For complicated formulas, this can save a lot of time.
Alt + F
to open the formula editor at your mouse location.4 + 5
and hit Ctrl + Enter
to confirm the formula.Now, for a slightly more complicated formula:
Alt + F
again to create a new formula.a * b + (c - d)
and hit Ctrl + Enter
to confirm the formula.This time, multiple nodes are created, which together represent the formula.
Finally, let us look at an example using not just math nodes.
NOTE This one will only work in the shader nodes editor.
Alt + F
.te
in the promptTab
to autocomplete.tex_coords()
with your cursor in between the parentheses.)
to move the cursor out of the auto-closed parentheses. You can also use the arrow keys..
.Tab
again to autocomplete.Tab
until object
is suggested.tex_coords().object * 2
.This creates a "Texture Coordinate" node and connects the "Object" output of the node to a "Vector Math" node set to "Scale".
If you encounter any problems/bugs, or have a question you can create an issue or discussion on the GitHub page for the add-on code: https://github.com/WannesMalfait/Blender-Add-ons/issues
The main usage of the add-on is as follows:
Alt + F
to open the formula editor.Ctrl + Enter
to confirm, and add the nodes to your node tree.Your hands don't need to leave the keyboard during this process, so it can be a very fast way to add nodes.
Let us now look at the "syntax" of the formulas you can type:
NOTE These examples are for the Geometry Nodes editor, but most of it is applicable to shaders too. The syntax is based on a combination of python and typescript.
The following example illustrates the basics of the syntax.
selection = dot(normal(), {1,0,1}) > 5;
uv_sphere = uv_sphere(18,20);
g1, g2 = separate_geometry_point(uv_sphere, selection);
x, y = position();
set_position(g1,_, {x, y, 1/(x*y)});
Let us explain what is going on line by line:
selection = dot(normal(), {1,0,1}) > 5;
This creates a variable selection
, and assigns it to expression on the right-hand side of the equals sign.
The right-hand side computes the dot product between the normal and the vector {1,0,1}, and then checks if that is greater than 5.
The function dot
is a shorthand for vector_math_dot_product
.
Furthermore, the variable is assigned a "type". The different possible types correspond to the different types of node sockets, e.g. float
, boolean
, vec3
, geometry
, ...
In this case, the type of selection will be boolean
, because >
returns a boolean
.
uv_sphere = uv_sphere(18,20);
Here we create a new variable, uv_sphere
which is assigned to the output of the uv_sphere
function.
The uv_sphere
node usually also takes a radius as input, but because it was
omitted the node socket will be left as default.
If we only wanted to set the radius and leave the rest as default we could do:
uv_sphere = uv_sphere(_,_, 2.0);
uv_sphere = uv_sphere(radius = 2.0);
The second option is useful if there are lots of arguments you want to skip.
The uv_sphere
variable has type geometry
.
g1, g2 = separate_geometry_point(uv_sphere, selection);
The "Separate Geometry" node has a drop-down selection for the domain.
For each domain, there will be a corresponding function.
Since we want the Point
domain, we use the separate_geometry_point
function.
The "Separate Geometry" node outputs two geometries.
We assign these to the variables g1
and g2
respectively.
Note how we used the previously created uv_sphere
and selection
variables as inputs.
x, y = position();
The position
function returns a vec3
.
We can automatically destructure it into its x,y and z components by assigning it like we did here.
Note that the z component is discarded since we didn't assign it to any variable.
set_position(g1,_, {x, y, 1/(x*y)});
We update the position of g1
, using the "Set Position" node.
The selection input is ignored.
Many of the common operations are possible for different types. The add-on tries its best to infer types from expressions to determine the best match. This is best seen with some examples:
x = 10; // x is now of type 'int'
// The integer math node will be used, since we are working in the geometry nodes editor.
y = 5 * x; // The type of y will be 'int'.
// The best match is a 'Vector Math' node set to scale
z = {1,2,3} * y; // The type of z will be 'vec3'
// For 'x < y' a Compare node set to float is used.
// For 'z < 5' a Compare node set to vector is used.
// The 'or' is treated as a Boolean Math node set to 'or'.
test = x < y or z < 5; // The type of test will be 'boolean'.
// 'a' has not been defined, but its type can be inferred.
// The simplest possibility is for 'a' to be a 'float' to add with another 'float'.
// Hence, 'a' is seen as type 'float' and a 'Value' node is added named 'a'.
b = a + 1.5;
d = c and b < a; // Similarly, 'c' will be of type 'boolean' here.
Remark: When the type of some variable can't be inferred, it is assumed to be a 'float'.
In blender, you can type python expressions in input fields. You can also do this here by using #
. The expression after #
is evaluated immediately.
example1 = sin(#tau/4)
example2 = sin(#(tau/4))
The difference between the two examples is the following:
tau
is evaluated as a
python expression.tau/4
is evaluated as a
python expression.When writing a formula we like to think from left to right. However, when we want to compose multiple functions we now have to think from right to left. Say that we want to scale some vector pos
and then apply fract
to that, we would have to write: fract(scale(pos, 0.5))
or fract(pos * 0.5)
. Notice that the last thing we wanted to do was the first thing we had to write. To prevent this problem you can also write the following: pos.scale(0.5).fract();
. Calling .function()
will take the expression before the .
and place it in the first argument. For example: pos.scale(0.5)
is the same as scale(pos, 0.5)
. This can feel a lot more natural, because it is also the way we usually build node trees.
Another thing that can be annoying is getting the specific output of a node with many outputs. Take for example the Texture Coordinate node. If we want to get the object output you would have to write: _,_,_, object = tex_coord();
. To fix this you can also use the .
to get a specific output. So we could write object = tex_coord().object
. Combining this with the chaining of function calls we get a concatenative way of writing expressions:
tex_coord().object.scale(0.5).fract().sub(0.5).abs() ...
If you have something where you need to iterate a node group multiple times (like in ray marching), then you can use a loop. It just repeats its body a given number of times.
// Equivalent to doing 10.sin().sin().sin().sin()
x = 10;
loop 4 {
x = sin(x);
}
If you need the index of the current iteration as an input, that is also possible:
// i will go from -1 to 1 (inclusive).
loop i = -1 -> 1 {
loop j = -1 -> 1 {
x = {i,j}; // Creates a combine XYZ with values {i,j, 0}
}
}
The general syntax for a loop is:
loop iteration_variable = start -> end {
// Loop body
}
NOTE The repeat/for each zones are not yet supported, but will be in the future.
What if you want to add your own function? This is also possible with "custom implementations". In the preferences you'll find a link to the folder that contains these implementations. There are different types of files in the folder:
What do these "implementations" look like? The general form is like this:
fn function_name(input: type, ..., input: type) -> output: type,..., output: type {
// function body.
}
Let's look at an example:
fn _and(a: float, b: float) -> c: float {
// This defines the "and" operation for two floats.
// The reason for the underscore is that `and` is a reserved keyword.
out c = a * b;
}
When you create functions with names like 'add', or 'mul', the corresponding operator will also work with these implementations.
fn pow(a: vec3, b: float) -> c: float {
ax,ay,az = a;
out c = {ax**b, ay**b, az**b};
}
{1,2,3} ** 5; // This executes the function above
If you want to create a node group instead of a function, you can simply replace the fn
with ng
ng asinh(x: float) -> y: float {
// The node group `asinh` takes a float "x" as input and returns a float "y".
out y = log(x + sqrt(1+x*x), #e); // Here we set the output "y" using the `out` keyword.
}
v2.0.4 Another fixes release for review comments.
This extension requests the following permissions:
Loading and writing custom implementations
Pasting formulas from clipboard
The best add-on to build math nodes! I had to do some custom 3D math computations with Octonions and Quaternions which deal with a lot of math calculations. This add-on did it with no problems!