# Writing a Shader Module

We're going to write a Module which works with the built-in shader Renderer.

To follow this guide, we'd recommend having some experience with:

  • JavaScript (ES6+)
  • GLSL
    • if you don't already have experience with GLSL then we recommend ???

We'll be stealing 😬 a shader (opens new window) from Martin Splitt (opens new window)'s awesome shaderpad (opens new window) - so you won't need a bunch of GLSL experience to follow along.

# 1. Create a new file

Save a blank JavaScript file in the Media Manager's media directory. This will need to be placed in a module folder within a Project folder. e.g. [media path]/[project]/module.

By saving your Module here the Media Manager will compile your code and send it to modV on every file save. If you've placed your Module within a Layer already, you'll need to remove it from the Layer and drag your Module in again from the Gallery to use the updated Module.

# 2. Export an Object

Let's get started by exporting an Object. modV Modules are written out as a plain Object.

export default {

};

# 3. Set up the Meta

Next up, we'll need to describe our Module with a meta Object block. Let's define the Module type as shader and give our Module a name.

export default {
  meta: {
    // this tells modV our Module should be used with the shader renderer
    type: 'shader',

    // our Module's name
    name: 'Spherical',
  },
};

# 4. Define our Shaders

In shader type Modules, there are two properties on the Module body to define the Shaders we want to use:

  • fragmentShader
  • vertexShader

Both are optional and the shader Renderer automatically detects whether we're using #version 300 es or not. Both of these variables accept Strings only.

If you have a larger shader or require syntax highlighting, you may import your shaders using:

import fragmentShader from 'circles.frag';
import vertexShader from 'circles.vert';

We'll only be using the fragmentShader property in this guide:

export default {
  // meta: { ... },
  
  fragmentShader: `
    precision mediump float;
    varying mediump vec2 uv;

    uniform float uA;
    uniform sampler2D u_modVCanvas;

    void main(void) {
      vec2 p = -1.0 + 2.0 * uv.xy;
      float r = sqrt(dot(p, p));

      float f = sqrt(1.0 - (r * r));
      bool toggle = mod(r, 0.1) > 0.05 ? true : false;
      if (f > 0.0 && toggle) 
        gl_FragColor = vec4(vec3(f), 1.0) * texture2D(u_modVCanvas, vec2(uv.x, uv.y));
      else 
        gl_FragColor = vec4(uv.x, uv.y, 1.0, 1.0);
    }
  `,
};

# 5. Props and Uniforms

# 5.1 uniforms

The shader Renderer defines the following Uniforms for you to consume within your Shader:

name type info
u_modVCanvas, iChannel0, iChannel1, iChannel2, iChannel3 texture2D The incoming frame
u_delta, iTime, iGlobalTime float Time in milliseconds
u_time float Time in seconds
iFrame float Frame count since modV's drawloop started
iResolution vec3 The width, height and pixel density of the largest Output Window
iMouse vec4 [0.0, 0.0, 0.0, 0.0] @todo make this an actual control

Shadertoy compatibility

The shader Renderer attempts to provide basic Shadertoy Uniform compatibility.
Currently it does not support passes/buffers or Audio shaders.

# 5.2 Defining props

With a shader Module, the props should match the uniforms in your shader.

Our shader above has a uniform named uA so we'll define the same in our props:

props: {
  uA: {
    type: 'float',
    default: 1.0,
    min: 0.0,
    max: 2.0,
  },
},

# 6. Putting everything together

The following code puts together everything from above:

export default {
  meta: {
    type: 'shader',
    name: 'Spherical',
  },
  
  props: {
    uA: {
      type: 'float',
      default: 1.0,
      min: 0.0,
      max: 2.0,
    },
  },

  fragmentShader: `
    precision mediump float;
    varying mediump vec2 uv;

    uniform float uA;
    uniform sampler2D u_modVCanvas;

    void main(void) {
      vec2 p = -1.0 + 2.0 * uv.xy;
      float r = sqrt(dot(p, p));

      float f = sqrt(1.0 - (r * r));
      bool toggle = mod(r, 0.1) > 0.05 ? true : false;
      if (f > 0.0 && toggle) 
        gl_FragColor = vec4(vec3(f), 1.0) * texture2D(u_modVCanvas, vec2(uv.x, uv.y));
      else 
        gl_FragColor = vec4(uv.x, uv.y, 1.0, 1.0);
    }
  `,
};