Skip to content
WannesMalfait edited this page Sep 10, 2023 · 6 revisions

This document contains all the information for users of the add-ons.

Installation

To install the add-on, there are two possibilities:

  • For every add-on other than "Math Formula" you go to the home page of this repository.
    • There you click on the relevant file of the add-on, e.g. for the "L-system" add-on, you click on L-system.py. For the Supershape add-on, the SuperShape2-80.py is for blender versions 2.80+. The other file, SuperShape.py is for version 2.79b.
    • This shows the code of that add-on (you should end up here for the L-system add-on).
    • You can now click on the "Download raw file" button in the top right. Save the file somewhere on your system.
    • Open blender and in the preferences window (Edit -> Preferences), go to "Add-ons". Click the "Install..." button on the top. Select the file you just downloaded to install the add-on.
  • For the "Math Formula" add-on go to the releases tab.
    • Find the release that is most relevant to your version of blender. (Select the latest release for your version of blender).
    • Under "Assets", you'll find a zip file called math_formula.zip. Download this file to somewhere on your system.
    • Open blender and in the preferences window (Edit -> Preferences), go to "Add-ons". Click the "Install..." button on the top. Select the zip file you just downloaded to install the add-on.

Documentation per add-on

L-system

This add-on generates fractal structures using Lindenmayer systems. I made a YouTube video showcasing the add-on. Common examples of L-systems can be found on this wikipedia page.

Once installed, you can find the option "Add Fractal" under the "Add" menu (⇧ Shift + A) in the "Mesh" category.

Supershape

This add-on generates a surface using the "Superformula". I have an (old) video showcasing the add-on in blender. This page has a more in-depth explanation of what this formula is about, and what the different parameters do.

Times Table

An add-on for creating a visualization of mathematical "times tables". The add-on was inspired by Mathologer's video. I made a short video showcasing the add-on.

Value Finder

This add-on contains utilities for finding the best value for some input to your shader. Once installed, you can find it in the Node Editor side panel ( N panel) in the "Value Finder" tab.

Features

  • 2D/3D Setup: Creates a Plane/Sphere with the current material, and sets up a camera to view just that object. Used for rendering "2D/3D" textures. Use the "Isolate Collection" button to ensure you will only render this setup
  • Value Finder: For the active node, you can choose one of the inputs. This operator will then create renders for each of the input values configured in the settings.

Parametric

Add a parametric surface. Once installed, it can be found in "View 3D > Add > Mesh > Add a Parametric Surface". The add-on will generate the surface generated by the given parametrization. The input domain for the parameters u and v can be configured. The helper variables can be used to re-use parts of the equation that are repeated. The Wikipedia page gives some examples of parametric surfaces.

Math Formula

An add-on to speed up adding nodes to shader or geometry nodes. In essence it allows you to type some code that will then be converted into a node setup. Additionally it has some tools to organize node trees.

Features

Note that some features may only be available in the latest version of the add-on.

  • Positioning/Selecting node trees. Make sure that some node is active.
    • Pressing E will arrange the nodes connected to the selected node. This will only move parents (i.e. nodes to the left) of the active node. The Alt modifier can be used to limit to only selected nodes (and the active node). The ⇧ Shift key can be used to move only children of the active node (i.e. nodes to the right) instead of parents.
    • Pressing K will select parents and children of the active node. The ⇧ Shift modifier limits to children, and the Ctrl modifier limits to parents.
  • Creating node trees from code. This can be done through the editor activated by the Alt + F shortcut. More details below.

Quick start

  1. Go to the shader node editor, and open an existing node tree.
  2. Use the keyboard shortcut Alt + F to open the formula editor at your mouse location.
  3. Type 4 + 5 and hit Ctrl + ⏎ Enter to confirm the formula.
  4. A math node set to "Add" is added at the mouse location, with as input values "4" and "5".

This time, let's try something a bit more complicated.

  1. Hit Alt + F again to create a new formula.
  2. Type a * b + (c - d) and hit Ctrl + ⏎ Enter to confirm the formula.
  3. Multiple nodes are created, which together represent the formula. The final result should look something like this:
graph LR;
A --> Multiply;
B --> Multiply;
C --> Subtract;
D --> Subtract;
Multiply & Subtract --> Add;

This final example showcases the autocomplete capabilities of the editor, and how it can really be much faster than adding nodes manually.

  1. Open the formula editor once more.
  2. Type te in the prompt
  3. Press Tab to autocomplete.
  4. It should now show tex_coords.
  5. Add (). to the end, so the formula becomes tex_coords().
  6. Hit Tab again to autocomplete.
  7. Keep pressing Tab until object is suggested.
  8. Finish the formula by multiplying by 2: 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".

Here is a more detailed description of the types of formulas you can add.

Syntax

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_points(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 unconnected. If we only wanted to set the radius and leave the rest as default we could do:

  • Option 1: uv_sphere = uv_sphere(_,_, 2.0);
  • Option 2: 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_points(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 Points domain, we use the separate_geometry_points 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.

Function overloading

Many of the common operations are possible for different types. The math formula 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'
// There is no multiplication for integers yet, so the 'Math' node set to 'Multiply' is used instead. 
y = 5 * x; // The type of y will be 'float'
// 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 + 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'.

Python expressions

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:

  • In the first example a divide node is created, because only tau is evaluated as a python expression.
  • In the second example no divide node is created because tau/4 is evaluated as a python expression.

Chaining calls

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.

Getting specific outputs

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() ...

Loops

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
}

Custom Implementations

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:

  • Files that start with "cache": These are used by the add-on to load them faster. You should not change these.
  • Files that end with "sh": These are implementations specific to shader nodes.
  • Files that end with "gn": These are implementations specific to geometry nodes.
  • Other files apply to both shader and geometry nodes.

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:

// In 00_general
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

// In 00_general
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.
}

NOTE: The node group is only created when the "function" is called.

Shortcuts

The following shortcuts are available in the node editor. The keybindings can be changed in the preferences.

Keybinding Modifier Effect
Alt + F Open the formula editor at the mouse location
E Arrange parent nodes of the active node
- Alt Limit arranging to selected nodes
- ⇧ Shift Arrange child nodes instead of parent nodes
K Select parents and children of active node
- ⇧ Shift Only select children
- Ctrl Only select parents

In the formula editor there are also a few shortcuts that could be useful. These are hard-coded for now.

Keybinding Effect
Arrow keys Move the cursor around
Ctrl + Up/Down arrow Navigate formula history
Tab Try to auto-complete. Cycles through different suggestions on repeated usage
Ctrl + V Paste clipboard contents at cursor location
Home Move cursor to the start of the line
End Move cursor to the end of the line
Alt + C Check for any errors
Esc / Right mouse click Exit the editor without executing the formula
Ctrl + Enter Execute the formula and exit the editor
Middle mouse button drag Move the editor around
Scroll up and down Increase or decrease font size