Back to shaders
Shader test bench
Doodler Overlay.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
uniform sampler2D inputImage;
uniform int penDown;
uniform int eraseMode;
uniform int eraseAndReset;
uniform float penRate;
uniform vec4 drawColor;
uniform float penSize;
uniform vec2 penLoc;
uniform float dirtyTip;
uniform vec4 bgColor;
precision mediump float;
/*
{
"CATEGORIES" : [
"Overlay"
],
"DESCRIPTION" : "Uses a virtual pen to draw colors on top of an image.",
"ISFVSN" : "2",
"INPUTS" : [
{
"NAME" : "inputImage",
"TYPE" : "image"
},
{
"NAME" : "penDown",
"TYPE" : "bool",
"DEFAULT" : 1,
"LABEL" : "Pen Down"
},
{
"NAME" : "eraseMode",
"TYPE" : "bool",
"DEFAULT" : 0,
"LABEL" : "Eraser Mode"
},
{
"NAME" : "eraseAndReset",
"TYPE" : "event",
"LABEL" : "Erase All"
},
{
"NAME" : "penRate",
"LABEL" : "Auto Rate",
"TYPE" : "float",
"MAX" : 1,
"DEFAULT" : 1,
"MIN" : 0
},
{
"NAME" : "drawColor",
"LABEL" : "Pen Color",
"TYPE" : "color",
"DEFAULT" : [
0,
0,
1,
0.75
]
},
{
"NAME" : "penSize",
"LABEL" : "Tip Size",
"TYPE" : "float",
"MAX" : 0.2,
"DEFAULT" : 0.01,
"MIN" : 0
},
{
"NAME" : "penLoc",
"LABEL" : "Pen Start",
"TYPE" : "point2D",
"MAX" : [
1,
1
],
"DEFAULT" : [
0.5,
0.5
],
"MIN" : [
0,
0
]
},
{
"NAME" : "dirtyTip",
"LABEL" : "Texture",
"TYPE" : "float",
"MAX" : 1,
"DEFAULT" : 0.5,
"MIN" : 0
},
{
"NAME" : "bgColor",
"LABEL" : "Eraser Color",
"TYPE" : "color",
"DEFAULT" : [
0,
0,
0,
0
]
}
],
"PASSES" : [
{
"WIDTH" : "1",
"HEIGHT" : "1",
"TARGET" : "bufferPosition",
"PERSISTENT" : true,
"FLOAT": true
},
{
"TARGET" : "lastBuffer",
"PERSISTENT" : true,
"FLOAT": true
},
{
}
],
"CREDIT" : "VIDVOX"
}
*/
float seed = 1.239;
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec4 returnMe = vec4(0.0);
bool doReset = false;
if ((eraseAndReset)||(FRAMEINDEX<=1))
doReset = true;
// update the pen position for auto-walk if needed
if (PASSINDEX==0) {
// if needed, reset this to the default pen position
if (doReset) {
returnMe = vec4(penLoc.x,penLoc.y,0.0,1.0);
}
// get the last position, move randomly by up to 1 pixel
else {
returnMe = IMG_THIS_PIXEL(bufferPosition);
float newDirection = rand(vec2(u_time,seed));
vec2 shift = vec2(0.0,0.0);
if (newDirection < 0.25)
shift = vec2(1.0,0.0);
else if (newDirection < 0.5)
shift = vec2(-1.0,0.0);
else if (newDirection < 0.75)
shift = vec2(0.0,1.0);
else
shift = vec2(0.0,-1.0);
shift = shift * penSize * penRate;
returnMe.rg += shift;
if (returnMe.r > 1.0)
returnMe.r = 1.0;
else if (returnMe.r < 0.0)
returnMe.r = 0.0;
if (returnMe.g > 1.0)
returnMe.g = 1.0;
else if (returnMe.g < 0.0)
returnMe.g = 0.0;
}
}
// draw into our overlay buffer
else if (PASSINDEX==1) {
if (doReset) {
// reset to our bgColor
//returnMe = vec4(0.0,0.0,0.0,0.0);
returnMe = bgColor;
}
else {
vec4 lastPos = texture2D(bufferPosition, vec2(0.0));
if ((penDown)&&(distance((gl_FragCoord.xy / u_resolution.xy),lastPos.rg) < penSize)) {
// if we are erasing, replace with the bgColor
returnMe = (eraseMode) ? bgColor : drawColor;
if (dirtyTip > 0.0) {
float dirtRand = rand((gl_FragCoord.xy / u_resolution.xy)*vec2(u_time,1.0+u_time));
if (dirtRand <= dirtyTip) {
returnMe = texture2D(lastBuffer, (gl_FragCoord.xy / u_resolution.xy));
}
}
}
else {
returnMe = texture2D(lastBuffer, (gl_FragCoord.xy / u_resolution.xy));
}
}
}
// draw the overlay on top of the incoming image
else {
vec4 overlayPixel = texture2D(lastBuffer, (gl_FragCoord.xy / u_resolution.xy));
vec4 bgPixel = texture2D(inputImage, (gl_FragCoord.xy / u_resolution.xy));
// use the alpha of the overlay to mix between the two pixels
returnMe.rgb = mix(bgPixel.rgb, overlayPixel.rgb, overlayPixel.a);
// use the same alpha as the input image for the final result
returnMe.a = bgPixel.a;
}
gl_FragColor = returnMe;
}