Watch out, this tutorial covers features that are only available in Live and Trial. Start users, you can upgrade now!

 

Step by step

Adding a shader in HeavyM

DOWNLOAD SHADERS

 

In order to add a shader in HeavyM, you first have to add a player by dragging and dropping the corresponding icon from the toolbar to your work area. A rectangular shape should appear in your work area and a parameters panel on the left.

 

 

You can open this panel by clicking on a player and leave it always open by clicking on the pin icon. If you want to know more about how players work in general, you can consult our tutorial on the subject.

By default, the player mode is set on media. In this mode, you can only add a picture or a video. To set the player in shader mode, you have to click on the corresponding icon in the settings section of your player.

 

You can then select your shader by clicking on this icon +. It will allow you to see the available files on your computer and search for your fragment shader. You must chose a file with the .frag extension.

 

Understanding how shaders work

What’s a shader?

A shader is a program that communicates directly with your graphic card to render an image. You can find a lot of tutorials and explanations about Shaders on the web.

To use shaders in HeavyM, you don’t have to understand all the details, but the basics will help. In HeavyM, we only use fragment shaders. To keep it simple, let’s say it’s a little program that renders the color of each pixel of the image you want to display. To make those programs work, you must have a graphic card that supports OpenGL 2.0.

 

Ouput color

void main( void )
{
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

This is a short example that puts the red color on each pixel. You can already try this code in HeavyM. Copy/Paste this code in a text editor and save it with the .frag extension. Then, load it into a HeavyM player as described before.

Now, if you click on Play and Activate on Top in your players panel, your player will display a red image.

 

 

Explanation: each pixel is rendered by this code. gl_fragColor is the name given to the color we get. We assign it an RGBA vector with some values between 0 and 1 (in our example, we have red, which is : R = 1, G = 0, B = 0, A = 1).

Now you can try to change those values in order to get other colors.

 

Input Position

We are are going to see how we can have different colors on different pixels. We can modify the color of the pixel depending on its position. This position is called gl_FragCoord, and if we want to get the X or Y position of the pixel, we have to use gl_FragCoord.x and gl_FragCoord.y.

A simple effect is to vary the RGB values based on the position of the pixel, in order to get a gradient:

void main( void )
{
      float red = gl_FragCoord.y / 1000.0;
      float blue = gl_FragCoord.x / 1000.0;
      gl_FragColor = vec4(red, 0.0, blue, 1.0);
}

In this code, we declare a number red, equal to the Y position of the pixel divided by 1000. We do the same for the blue, equal to the X position divided by 1000.
Therefore, the pixel located at (0,0), on the top left side, gets red and blue values equal to 0, so it will be black, whereas the pixel located on the bottom right gets red and blue values equal to 1, so it will be purple (red + blue).

Here, we divide by 1000 because we want some values between 0 and 1 (it depends on the resolution of your shader).

 

You can then apply a lot of mathematical transformations on your colors to get some visual effects. To understand this part, we advise you to read some tutorials dedicated to fragment shaders on the web.

 

Uniform Values

However, all of this only allows you to display a fixed image. In a video mapping, it’s nice to have an animated image, so we have to take the time into account. In order to do this, we can send some values to a shader at each frame: those values are called Uniform values.

In HeavyM, we have decided to send 6 values:

  • HM_x, HM_y, HM_z and HM_speed : just integer numbers between 0 and 100 that you can modify from the interface.
  • HM_resolution: a vector (x,y) corresponding to your shader’s resolution.
  • HM_time: a float corresponding to the elapsed time since the projection was launched.

uniform float HM_x;
uniform float HM_y;
uniform float HM_z;
uniform float HM_speed;
uniform float HM_time;
uniform vec2 HM_resolution;

void main( void )
{
      float red = (gl_FragCoord.y / HM_resolution.y);
      float blue = (gl_FragCoord.x / HM_resolution.x);
      float green = (sin(HM_time) + 1.0) * 0.5;
      gl_FragColor = vec4(red, green, blue, 1.0);
}

First of all, we declare those uniform values, to tell the shader we are going to use them.
We can then use those values in the shader. In the example above, the green value is equal to the sinus of the time (so we have a value between -1 and 1), to which we add 1 and then multiply by 0.5, in order to have a final value between 0 and 1, that varies with HM_time.

 

 

Here is another example of a code which draws a circle that changes color based on time. The circle radius depends on the HM_x variable, that you can modify in the software.

uniform float HM_x;
uniform float HM_y;
uniform float HM_z;
uniform float HM_speed;
uniform float HM_time;
uniform vec2 HM_resolution;

void main(void)
{
         float x = (gl_FragCoord.x / HM_resolution.x) – 0.5;
         float y = (gl_FragCoord.y / HM_resolution.y) – 0.5;

         float rayon = HM_x / 100.0;

      float d = sqrt(x*x + y*y);
      float red;
      float mov = 0.5 * sin(HM_time);

      if( d < rayon)
      {
         red = 0.7 – 2.0 * d + mov;
      }
      else
      {
         red = 0.6 * d – mov;
      }
      gl_FragColor = vec4(red, 0.0, 0.4, 1.0);
}

 

 

Modifying an existing shader

If you want to be able to use shaders found on website like teractiveshaderformat.com or glslsandbox.com, you have to understand what’s happenning inside a little bit.

The first things to modify are the Uniforms values. They are chosen by the person who executes the shader. In HeavyM, we chose to put 6 uniform values and to name them in a certain way (see above). You have to adapt your shader, depending on those names, to get your values correctly from HeavyM.

Let’s take an example : http://glslsandbox.com/e#40449.0.

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 resolution;

void main( void ) {

      vec2 position = ( gl_FragCoord.xy * 2.0 – resolution) / min(resolution.x, resolution.y);
      vec3 destColor = vec3(1.0, 0.0, 0.8 );
      float f = 0.0;

      for(float i = 0.0; i < 50.0; i++){

         float s = sin(time + i ) ;
         float c = cos(time + i );
         f += 0.003 / abs(length(8.0* position *f – vec2(c, s)) -0.4);
      }

      gl_FragColor = vec4(vec3(destColor * f), 1.0);
}

In this code, glslsandbox calls those uniform values time and resolution. So you have to replace all the occurences of time by HM_time and all the occurences of resolution par HM_resolution.

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float HM_time;
uniform vec2 HM_resolution;

void main( void ) {

      vec2 position = ( gl_FragCoord.xy * 2.0 – HM_resolution) / min(HM_resolution.x, HM_resolution.y);
      vec3 destColor = vec3(1.0, 0.0, 0.8 );
      float f = 0.0;

      for(float i = 0.0; i < 50.0; i++){

         float s = sin(HM_time + i ) ;
         float c = cos(HM_time + i );
         f += 0.003 / abs(length(8.0* position *f – vec2(c, s)) -0.4);
      }

      gl_FragColor = vec4(vec3(destColor * f), 1.0);
}