Advanced Plugin

< All Topics

Advanced Plugin

Using the FFGL built-in variables and parameters, it is possible to create complex and interesting plugins. The simplest way to build advanced plugins is using a GLSL-based tool like Shadertoy to write a GLSL shader and convert it to an FFGL plugin. On this page we will recreate the Flames plugin that is included with Morph Pro.

 

 

Shadertoy

 

Shadertoy is a very useful platform to experiment with GLSL shaders and test ideas before writing FFGL plugins.

 

Writing a Shader

 

Once you have a Shadertoy account you can create a GLSL shader.

 

On the top right of your home page, click New to make a new shader. You should now have a default shader with a color gradient as its output on your webpage.

 

Most shaders use basic built-in GLSL Trigonometric functions like sin, cos and tan to create complex visuals. These functions create waveforms that can be used to modify various parameters such as colors over time.

 

The Shadertoy code to recreate the flames plugin is:

 

//1- Built-in variables
#define time itime
#define resolution iResolution
//2- Parameters
#define Background vec3(0.0,0.0,0.0)
#define Degree 10.1//0.1-10.0
#define Color vec3(0.75,0.25,0.25)//0.1-2.0
#define uv vec2(gl_FragCoord.xy/resolution.xy)//Normalized pixel coordinates (from 0-1)
#define M_PI 3.14
float tanWave( float x, float a, float f, float tX, float tY )
{
float y = a*0.5*(tan(2.0*M_PI*f*(x-tX))+1.0+tY);
return y;
}
float cotanWave( float x, float a, float f, float tX, float tY )
{
float y = 1.0/tanWave(x,a,f,tX,tY);
return y;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
//Wave1
float a1 = 0.25;
float f1 = 2.0;
float tX1 = 0.1;
float tY1 = 0.0;
//Wave2
float a2 = 0.1;
float f2 = 4.0;
float tX2 = 0.5;
float tY2 = 0.5;
//Wave3
float a3 = Degree;
float f3 = 0.5*cos(Speed*iTime);
float tX3 = 0.0;
float tY3 = 0.0;
//Output wave
float x = uv.x;
float y = tanWave(x,a1,f1,tX1,tY1) * cotanWave(x,a2,f2,tX2,tY2);
y *= cotanWave(x,a3,f3,tX3,tY3);
float dist = distance(uv.y,y);
fragColor = vec4(mix(Color,Background,dist),1.0);
}

 

The core of this shader are the tanWave and cotanWave functions that are used to create a color variation based on which pixel is currently being rendererd onscreen. These functions take the following parameters:

 

  • x: coordinate
  • a: amplitude
  • f: frequency
  • tX: horizontal translation
  • tY: vertical translation)

 

By experimenting with what values are passed to these functions and how the results are processed, interesting shapes can be created. Creating a shader is the result of trial and error with these functions to create unique results.

 

You can experiment with this shader to create your own variation. A few other interesting waveform functions to use are:
 

float sineWave( float x, float a, float f, float tX, float tY )
{
float y = a*0.5*(sin(2.0*M_PI*f*(x-tX))+1.0+tY);
return y;
}
float cosineWave( float x, float a, float f, float tX, float tY )
{
float y = a*0.5*(cos(2.0*M_PI*f*(x-tX))+1.0+tY);
return y;
}
float logWave( float x, float a, float f, float tX, float tY )
{
float y = a*0.5*(log(f*(x-tX))+1.0+tY);
return y;
}
float sqrtWave( float x, float a, float f, float tX, float tY )
{
float y = a*0.5*(sqrt(f*(x-tX))+1.0+tY)
return y;
}
float circle( float x, float a, float f, float tX, float tY )
{
float y = sqrt(pow(a,2.0)-pow(f*(x-tX),2.0)) + tY;
return y;
}

 

Notice that we have used the #define directive to declare global variables time and resolution instead of using the Shadertoy variables iTime and iResolution. We also used #define to declare the parameters Background, Degree, Speed and Color that we used to create the shader.

 

Writing the shader in this format will make it easy to convert it to an FFGL plugin as we’ll see below.

 

Converting to FFGL

 

There are three simple steps to convert any Shadertoy shader to an FFGL shader:

 

  • Change the shader to match the FFGL Format shown in the example
  • Remove built-in variable and parameter definitions
  • Add FFGL parameters

 
Using the shader above as an example, we have already written the code in the FFGL Format. We used built-in FFGL variables time and resolution instead of Shadertoy’s iTime and iResolution and used #define to map these to their Shadertoy equivalents.

 

Below is a list of Shadertoy built-in variables and their FFGL equivalents:

 

  • iResolution – resolution
  • iTime – time
  • iTimeDelta – deltaTime
  • iFrame -> frame
  • iChannelTime -> N/A
  • iMouse -> N/A
  • iDate -> N/A
  • iSampleRate -> N/A
  • iChannelResolution -> N/A
  • iChanneli -> N/A

 

Another part off folllowing the FFGL format is using #define to declare any parameters we plan on providing for the user to change.

 

With the first step complete, it is time to move on to step two and remove the first two blocks of code with these definitions.
 
This is necessary because the FFGL SDK already declares and adds the built-in definitions to the shader as uniforms by default and similarly adds parameter definitions when the AddParam function is called.

 

After removing the first two blocks, it is time to move on to step three and add the FFGL parameter definitions in the constructor of the plugin:

 

Flames::Flames()
{
//Input properties
SetMinInputs(0);
SetMaxInputs(0);
//Parameters
AddParam( Param::Create( “Background”, FF_TYPE_RED, 0.0f ) );
AddParam( Param::Create( “Background_green”, FF_TYPE_GREEN, 0.0f ) );
AddParam( Param::Create( “Background_blue”, FF_TYPE_BLUE, 0.0f ) );
AddParam( Param::Create( “Color”, FF_TYPE_RED, 0.0f ) );
AddParam( Param::Create( “Color_green”, FF_TYPE_GREEN, 0.25f ) );
AddParam( Param::Create( “Color_blue”, FF_TYPE_BLUE, 0.25f ) );
}

 

It is important to note that Param::Create adds a standard parameter with the default range of 0.0-1.0 while ParamRange::Create adds a standard parameter with a custom range. This finetunes exactly how a frame is rendered.

 

Another important note is why the three components of an RGB color parameter are added one by one instead of using AddRGBColorParam. This allows the default value of the color parameter to be specified instead of using the predefined value used by AddRGBColorParam.

 

In the next topic we will learn how to adapt our advanced plugin to react to music.

 

Previous: Basic Plugin

Next: Audio Reactive Plugin