YOUR ACCOUNT

Loop

The Loop component lets you render a subtree of components multiple times and combine results of all these iterations into a single output image.

Essentially, Loop allows a subtree of components to be 'plugged into itself' multiple times, taking its own output as an input. That is, Loop provides the user interface and internal logic (most importantly, the termination condition) for circular connections. Otherwise, such connections are not allowed by Filter Forge.

The Loop component cannot work without its slave components. Their output changes depending on the current Loop iteration – this means that you can use them to vary any number of parameters in the loop subtree from iteration to iteration.

For more information about Loop's slave components, see 'Examples' and 'Using Slave Components' below. For general information about slave components, see Slave components.

Loop is a map component, it can be located in the Advanced category on the Components bar. This component can output HDR colors.

Accumulator: Map Input (HDR), Required

Defines the result of a loop iteration, which is then fed back to the Accumulated slave component to be recursively processed in the next loop iteration. Accumulator is a required input – to make the filter work, this input must be connected. This input accepts HDR colors.

Iterations: Numeric Input

Defines the number of iterations, or cycles, of the loop. That is, how many times the Loop subtree will process the combined output of all previous iterations.

Slave Components

These buttons create slave components linked to the currently selected Loop component. You can have multiple copies of each slave component except Accumulated.

Add Accumulated:
Adds a required Accumulated slave component that outputs the combined result of all previous Loop iterations, which is then combined with the result of the current iteration and fed into Loop's Accumulator input. This slave component is necessary for any Loop to work and cannot have multiple copies.

Add Position:
Adds an optional Position slave component that outputs the 'completion percentage' of the Loop as normalized iteration number converted to color.

Add Iteration:
Adds an optional Iteration slave component that outputs the number of the current iteration of the loop as an HDR color with RGB values corresponding to the number of current iteration (1, 2, 3...).

Add Randomizer:
Adds an optional Randomizer slave component that outputs a normalized random value (converted to color) that is unique for each loop iteration. Multiple Randomizers can have different Variation settings (i.e. random seeds).

Examples

The following example demonstrates a simple loop:

Multistar.ffxml

The above Loop works as follows:

  1. The number of iterations, 5 for the above example, is defined in the properties of the Loop component.
  2. The Accumulated slave component provides the combined result of all previous iterations to the loop subtree.
  3. The loop subtree, consisting of a single Free Polygon component in this example, modifies the accumulated result of all previous iterations to produce the current iteration. A Position slave component is used to make the star rotation angle different from iteration to iteration.
  4. After being formed by the loop subtree, the output of the current iteration is sent to Loop;
  5. And on the next iteration, it will appear at the output of Accumulated. Repeat from step 2 four more times.

You can have multiple slave components linked to a single Loop (except Accumulated, which cannot have multiple copies). This example uses multiple Randomizers, each with their own color range and variation (i.e. random seed) for randomizing multiple inputs of the Loop subtree:

Multiple Randomizers.ffxml

Using Slave Components

The Loop component cannot work without its slave components. The key idea to understand about slave components is that their output changes depending on the current Loop iteration. This means that you can use them to vary any number of parameters in the iterated subtree from iteration to iteration.

Slave components of Loop work properly only when the following conditions are met:

  1. Slave components must be connected to the subtree of the Accumulator input through a subtree of components, not directly, otherwise they will have no effect.
  2. There must be no bitmap-based components between the slave and the Accumulator input. Bitmap-based components (such as Blur or Motion Blur) kill the element data sent to slaves by their master, and thus cannot be used in slave-to-master connections.

Nested Loops

Loops can be nested. In the example below, an inner loop creates a row of 5 circles, which is then duplicated 5 times by the outer loop. Note that slave components of the outer loop can affect components in the subtree of the inner loop:

Nested Loops.ffxml

Optimizing the Speed of Loops

Technically, Loops are implemented via recursive calls to the sampling function of the subtree connected to the Accumulator input of the Loop component. This means it is possible to get an exponentially-growing recursion when the Loop subtree requests more than one sample from the Accumulated slave component. When you have an exponential recursion with a relatively high number of iterations in your filter (which can be as low as 7 or 8), the rendering times may grow so big that you may never see any rendered result.

Here's an example of manually unrolling a Loop with an exponential recursion.

The original filter:

The manually unrolled version:

That's 3 * (1 + 2 + 4 + 8 + 16 + 32 + 64) = 381 components that must be evaluated in order to render the result. Increase the iteration count from 7 to 8 and you will get 3 * (1 + 2 + 4 + 8 + 16 + 32 + 64 + 128) = 765 components: the number of components basically doubles with every additional iteration. If you change the loop subtree so that it sends three samples to Accumulated instead of two as shown above, you'll get 3 * (1 + 3 + 9 + 27 + 81 + 243 + 729 + 2187) = 9840 components to be evaluated.

Here are three rules of thumb for keeping exponential recursions in check:

  1. Keep the number of loop iterations as low as possible.
  2. Minimize the number of sampling calls to Accumulated. That is, all components in the Loop subtree between the Accumulated slave component and the Loop's Accumulator input should all have as few outgoing connections as possible:

  3. To avoid exponential recursions altogether, limit the number of samples to Accumulated to one per loop, as shown below. Such recursions unroll into neat linear chains of components, as opposed to ever-branching trees of exponential recursions:

Also note that limiting the number of samples to Accumulated won't help you deal with components that sample their inputs multiple times at different coordinates, such as Edge Detector and Derivative. Having a single such component in a loop between Accumulated and Accumulator guarantees an exponential recursion.

Limitations of Loops