Getting Started with Scripting
From Filter Forge Wiki
←Older revision | Newer revision→
Contents |
Introduction
A script is a program in a computer language that is executed within Filter Forge – more specifically, within Map Script and Curve Script components.
This article assumes that you are already familiar with the following:
- Basic concepts of programming such as control flow, conditional operators, loops, variables and values, function definitions, calls, arguments and return values etc.
- The Lua programming language, or at least with any programming language with roughly C-like syntax. Filter Forge help does not include information on the Lua language, so please refer to the Lua online manual at http://www.lua.org/manual/5.1/
- Basics of filter creation in Filter Forge, such as the Filter Editor, types of components (specifically, Map Components and Curve Components), and types of inputs (Map Inputs, Curve Inputs and Control Inputs.)
Examples
Example #1: Running Your First Script
This section is a quick tutorial that demonstrates running a simple script. We wish we could use the industry-standard "Hello World" example, but Filter Forge doesn't have string output functions, so we'll demonstrate a simple radial gradient generator:
Here's the code in a text form so you can copy/paste it into a Map Script component:
function prepare() end; function get_sample(x, y) d = math.sqrt (x*x + y*y) return d, d, d, 1 end;
Follow these steps to run this code in Filter Forge:
- Open the Filter Editor.
- Create a Map Script component (located in the Scripting category on the Component Bar).
- Click the Script tab.
- Delete the default script template in the editing area and paste the above script there.
- Click the Apply Changes button to execute the script.
Let's take a closer look at the the example script.
As you may have guessed, the image generation happens within the get_sample() function, which accepts two coordinates x and y as arguments and returns the output color calculated based on these coordinates. The color is returned as a list of four numbers, which represent RGBA channels. There's another function, prepare(), which normally handles initialization and precalculation, but in this particular script it is left empty – it does nothing.
To generate the gradient, the script calculates the distance between the sample point (given by the two coordinates) and the point with coordinates of 0, 0 (the top-left corner of the image); and simply returns the distance as a color.
Note that the sample script has no loops over pixels or any other form of iteration – it just specifies the image function to be called by the Filter Forge renderer or components. Filter Forge components don't produce images by painting their pixels directly. Instead, Filter Forge 'asks' a component "what color your image has at coordinates x and y?", and the component 'answers' with four RGBA values. In other words, the resulting image is implicitly defined by the component, as opposed to being explicitly painted by it. For more information on Filter Forge's approach to image generation, see Filter Forge's Sample-Based Architecture.
Example #2: Adding Inputs to a Script
Like other Filter Forge components, script components can have map inputs, control inputs and curve inputs. Unlike the built-in components that have a hard-coded set of inputs, script components let you define custom inputs via the Input Editor dialog. In this example, we will run a simple script that queries the state of a single checkbox input named Tick:
Here's the script. Note that it has a line that refers to an input named 'Tick':
function prepare() chk = get_checkbox_input(TICK) end; function get_sample(x, y) if chk then return 1, 1, 1, 1 else return 0, 0, 0, 1 end; end;
Follow these steps to get this script to run:
- Open the Filter Editor.
- Create a Map Script component (located in the Scripting category on the Component Bar).
- Click the Script tab.
- Delete the default script template in the editing area and paste the above script there.
- Click the Apply Changes button to execute the script.
Now you should see an error message in the Message Log. The error is shown because the script refers to an input named TICK but can't find an input with a Script Identifier of TICK in the Input Editor. Let's open the Input Editor to create this input, and try to run the script again:
- Click the Inputs tab.
- Click the Input Editor… button.
- Click the New Input… button and create a Checkbox input named 'Tick':
- Important: Make sure the Script Identifier of the input is TICK, or the script won’t run:
- Switch back to the Script tab and click Apply Changes – it works now!
Let's take a look at the script.
The prepare() function, which was empty in the first example, now includes a call to the get_checkbox_input() function and stores its returned value in a variable named chk. The only argument of the function is the Script Identifier of the checkbox input we want to query. This function call is the most important part of this example – it queries the checkbox input with the given Script Identifier, and returns its state as either true or false (in this case it means 'checked' or 'unchecked', respectively.)
In order to execute correctly, the Script Identifier passed to the function must match the Script Identifier specified for the input in the Input Editor. If there's no input in the Input Editor with such Script Identifier, or the input with the matching Script Identifier is not a checkbox input, the script will terminate with an error.
Different kinds of inputs are queried using different functions – checkboxes are accessed via get_checkbox_input(), sliders via get_slider_input() etc. For a full list of input types and their corresponding functions, see Referencing Inputs from Scripts.
Mappable inputs, such as Color Map Input, Grayscale Map Input and Curve Input, should be sampled by calling their corrresponding functions from within get_sample(), while control inputs, such as Checkbox Input or Slider Input, should be queried from prepare(), as shown in the example script above.
And lastly, the Input Editor displays code snippets showing you how to query or sample each type of input. These snippets will work when pasted into the script, so you don't have to remember the details or refer to this article every time you add an input. The default script template on the Script tab also includes snippets for every input you added before you first edited the script, but as soon as you edit the script for the first time, the template stops updating.
For detailed information, see Referencing Inputs from Scripts.
Example #3: Map Inputs and Sample Coordinates
This example is probably the most important – we'll be working with Map Inputs, which are the cornerstone of Filter Forge's power and flexibility. The script in this example implements a simple distortion component that has two map inputs – one for the source image, and another for the distortion map whose RGB channels will determine the direction and amount of distortion:
Here's the script. Note two calls to a get_sample_map() function:
function prepare() end; function get_sample(x, y) rd, gd, bd, ad = get_sample_map(x, y, DISTORTER) samplex = x + (rd + bd/2) * ad sampley = y + (gd + bd/2) * ad r, g, b, a = get_sample_map(samplex, sampley, SOURCE) return r, g, b, a end;
Follow these steps to run this script:
- Open the Filter Editor.
- Create a Map Script component (located in the Scripting category on the Component Bar).
- Click the Input Editor button and create two Color Map inputs named Source and Distorter.
- Important: Make sure the Script IDs of the inputs are SOURCE and DISTORTER, or the script won’t work.
Now the two inputs you created should appear as connectable slots on the component body in the filter editing area. You can connect some components to these inputs to better see the result. For example, you may connect Bricks to 'Source' and Perlin Noise to 'Distorter', as shown in the example picture above.
- Click the Script tab.
- Delete the default script template in the editing area and paste the above script there.
- Click Apply Changes or press [WIN]Ctrl+Enter or [MAC]Cmd+Enter to execute the script.
Let's look at the script.
The whole idea of this example is to demonstrate how map components (including map scripts) do their work by calling get_sample() functions of components connected to their inputs during the execution of their own get_sample() function.
The example script above issues two get_sample() calls. The first call obtains the RGBA values of the distortion map at the original sample coordinates specified by the x and y arguments of script's own get_sample() function, and the second call obtains the RGBA values of the source map at distorted coordinates, which are calculated before the call and stored in samplex and sampley variables. The RGBA values obtained at the coordinates after distortion are returned as the final color.
Note that the second get_sample() call uses different sample coordinates than the original sample coordinates supplied to script's own get_sample() via its x and y arguments. Filter Forge components, including scripts, are free to call get_sample() of their map inputs any number of times at any sample coordinates, and they can use the RGBA values returned by these calls in any manner, including the calculation of sample coordinates for subsequent get_sample() calls, as in the example script above. For more information on sampling and Filter Forge's coordinate system, see Filter Forge's Sample-Based Architecture.
Only map inputs need to be queried by calling their get_sample() function. Non-mapped inputs such as Checkboxes or IntSliders can't be queried via get_sample() – it doesn't make sense because non-mapped inputs always have the same values for any sample coordinates. It is more efficient to query these inputs within script's prepare() function, as shown in the Example 2 above. For more information, see Referencing Inputs from Scripts.
Example #4: Randomness via Variation
Local Variation slider as a good practice. The VARIATION variable is always set to 1 for components without the perk.
Basic Scripting Tips
Debugging Scripts
Print function. Message Log. Options.
Coordinate System for Sampling
Zero is the top-left corner.
Implementing Anti-Aliasing in Scripts
Filter Forge components are able to provide information about their zones that need to be anti-aliased. Scripts can return these zones as well. For details, see Implementing Anti-Aliasing in Scripts.
Namespaces, or Can Script Components Communicate?
They can't – scripts for all script components are executed in separate execution environments, so effectively they have separate namespaces. This means that you can freely litter the global Lua namespace with global variables without worrying about possible conflicts with other components.
Infinite Loops and Auto-Termination
Boom, headshot.
Defining Your Own Functions within Scripts
Yes, you can. Just don't use reserved names like get_sample etc – see help articles for appropriate script component to see what names it reserves. Scope will be the script component you're defining it in.
Bitmap-Based Map Scripts
Sorry, not yet.





