The twister effect is one of the most iconic demoscene effects. It simulates the twisting motion of a cuboid that is made of a soft, pliable material.

How does it work

The mathematical background behind this visual effect is suprisingly simple.

Imagine a cuboid sliced into thin wafers from top to bottom. For each slice, we essentially have a 2D quad, which is mathematically defined by four points: x0, x1, x2, x3. When we view the geometry standing in front of it, each of these slices will appear as a horizontal line.

The cuboid sliced into with wafers

Our image is a 2D plane and each of the slices essentially appears and behaves as a 1D line. All we can see is a horizontal line that maps to one of the edges of the respective quad.

For each quad, the 1D position of its points is calculated using the formula below:

\[x_i = sin(a + \frac{\pi}{2} * i )\]

where a is a phase value.

Each quad has 4 edges: x1-x2, x2-x3, x3-x4, x4-x1.

We need to draw all of the visible edges. Practically, in most cases there will be two edges visible. When a slice is aligned to 90 degree intervals, there will only be one edge visible.

The following condition can determine whether an edge is visible or not. Occluded edges are obviously not drawn.

\[x_{start} < x_{end}\]

A single slice, top and side views

Implementation

Below is an implementation of the twister effect and its live preview in shadertoy.

#define P2 1.57079632679

define E    (0.07)       // Edge width
define T    iTime        // Time
define C    vec3(.15)    // Background Color
define A    vec2(.5,1.5) // Amplitude XY
define S(x) texture(iChannel0, vec2(2.43, 1) * x).xyz // Texture

void mainImage(out vec4 c, vec2 p)

    vec2 u = p.xy/iResolution.xy*2.-1.;
    vec3 r = C;    
    float v[4];
    for (int i = 0; i < 4; ++i)
       v[i] = A.x * sin(A.y * sin(u.y * cos(T)) + (cos(T) + P2 * float(i)));
    for (int i = 0; i < 4; ++i) {
        float n = v[int(mod(float(i)+1.,4.))], p = v[i];
        if (n-p > 0. && u.x < n && u.x > p) {
            float k = n-p, x = (u.x-p) / k;
            r = k * S(vec2( x * A.x, u.y * A.y));                        
            float l = smoothstep(0., A.x * E, x) 
                    * smoothstep(0., A.x * E, 1.-x);
            r *= pow(l, 32.);
        }
    }
    c = vec4(r, 1);
}