# 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);
}
`,
};