Back to shaders
Shader test bench
Zooming Feedback.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
uniform sampler2D inputImage;
uniform vec2 preShift;
uniform float feedbackLevel;
uniform float rotateAngle;
uniform float zoomLevel;
uniform vec2 zoomCenter;
uniform vec2 feedbackShift;
uniform int invert;
uniform int blendMode;
uniform float blackThresh;
uniform float satLevel;
uniform float colorShift;
precision mediump float;
/*{
"CATEGORIES": [
"Retro",
"Feedback"
],
"CREDIT": "",
"DESCRIPTION": "Creates a simple zooming feedback loop",
"INPUTS": [
{
"NAME": "inputImage",
"TYPE": "image"
},
{
"DEFAULT": [
0.5,
0.5
],
"MAX": [
1,
1
],
"MIN": [
0,
0
],
"NAME": "preShift",
"TYPE": "point2D"
},
{
"DEFAULT": 0.9,
"MAX": 1,
"MIN": 0,
"NAME": "feedbackLevel",
"TYPE": "float"
},
{
"DEFAULT": 0.5,
"MAX": 1,
"MIN": 0,
"NAME": "rotateAngle",
"TYPE": "float"
},
{
"DEFAULT": 1.2,
"MAX": 2,
"MIN": 0.25,
"NAME": "zoomLevel",
"TYPE": "float"
},
{
"DEFAULT": [
0.5,
0.5
],
"MAX": [
1,
1
],
"MIN": [
0,
0
],
"NAME": "zoomCenter",
"TYPE": "point2D"
},
{
"DEFAULT": [
0.5,
0.5
],
"MAX": [
1,
1
],
"MIN": [
0,
0
],
"NAME": "feedbackShift",
"TYPE": "point2D"
},
{
"DEFAULT": 0,
"NAME": "invert",
"TYPE": "bool"
},
{
"DEFAULT": 3,
"LABELS": [
"Add",
"Over Black",
"Over Alpha",
"Max",
"Under Black",
"Under Alpha"
],
"NAME": "blendMode",
"TYPE": "long",
"VALUES": [
0,
1,
2,
3,
4,
5
]
},
{
"DEFAULT": 0.1,
"MAX": 1,
"MIN": 0,
"NAME": "blackThresh",
"TYPE": "float"
},
{
"DEFAULT": 1,
"MAX": 2,
"MIN": 0,
"NAME": "satLevel",
"TYPE": "float"
},
{
"DEFAULT": 0,
"MAX": 1,
"MIN": 0,
"NAME": "colorShift",
"TYPE": "float"
}
],
"ISFVSN": "2",
"PASSES": [
{
"PERSISTENT": true,
"TARGET": "feedbackBuffer"
}
]
}
*/
const float pi = 3.14159265359;
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
//vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
//vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
vec2 loc = (gl_FragCoord.xy / u_resolution.xy);
vec4 inputPixelColor = texture2D(inputImage, loc + (0.5 - (preShift)));
vec4 feedbackPixelColor = vec4(0.0);
// rotate here if needed
loc = loc * u_resolution;
float r = distance(u_resolution/2.0, loc);
float a = atan ((loc.y-u_resolution.y/2.0),(loc.x-u_resolution.x/2.0));
loc.x = r * cos(a + 2.0 * pi * rotateAngle - pi) + 0.5;
loc.y = r * sin(a + 2.0 * pi * rotateAngle - pi) + 0.5;
loc = loc / u_resolution + vec2(0.5);
vec2 modifiedCenter = zoomCenter;
loc.x = (loc.x - modifiedCenter.x)*(1.0/zoomLevel) + modifiedCenter.x;
loc.y = (loc.y - modifiedCenter.y)*(1.0/zoomLevel) + modifiedCenter.y;
loc += (0.5 - (feedbackShift));
if ((loc.x < 0.0)||(loc.y < 0.0)||(loc.x > 1.0)||(loc.y > 1.0)) {
feedbackPixelColor = vec4(0.0);
}
else {
feedbackPixelColor = texture2D(feedbackBuffer, loc);
}
feedbackPixelColor.rgb = rgb2hsv(feedbackPixelColor.rgb);
feedbackPixelColor.r = mod(feedbackPixelColor.r+colorShift,1.0);
feedbackPixelColor.g *= satLevel;
feedbackPixelColor.rgb = hsv2rgb(feedbackPixelColor.rgb);
if (invert)
feedbackPixelColor.rgb = 1.0 - feedbackPixelColor.rgb;
if (blendMode == 0) {
inputPixelColor = inputPixelColor + feedbackLevel * feedbackPixelColor;
}
else if (blendMode == 1) {
float val = inputPixelColor.a * (inputPixelColor.r + inputPixelColor.g + inputPixelColor.b) / 3.0;
inputPixelColor = (val >= blackThresh) ? inputPixelColor : feedbackLevel * feedbackPixelColor;
inputPixelColor.a = inputPixelColor.a + feedbackPixelColor.a * feedbackLevel;
}
else if (blendMode == 2) {
// apply the alpha to the input pixel as this happens
inputPixelColor.rgb = inputPixelColor.a * inputPixelColor.rgb + (1.0 - inputPixelColor.a) * feedbackLevel * feedbackPixelColor.rgb;
inputPixelColor.a = inputPixelColor.a + feedbackPixelColor.a * feedbackLevel;
}
else if (blendMode == 3) {
inputPixelColor.rgb = max(inputPixelColor.a * inputPixelColor.rgb, feedbackLevel * feedbackPixelColor.rgb);
inputPixelColor.a = inputPixelColor.a + feedbackPixelColor.a * feedbackLevel;
}
else if (blendMode == 4) {
float val = feedbackPixelColor.a * (feedbackPixelColor.r + feedbackPixelColor.g + feedbackPixelColor.b) / 3.0;
inputPixelColor = (val < blackThresh) ? inputPixelColor.a * inputPixelColor : feedbackLevel * feedbackPixelColor;
inputPixelColor.a = inputPixelColor.a + feedbackPixelColor.a * feedbackLevel;
}
else if (blendMode == 5) {
// apply the alpha to the input pixel as this happens
inputPixelColor.rgb = (1.0-feedbackPixelColor.a) * inputPixelColor.a * inputPixelColor.rgb + feedbackLevel * feedbackPixelColor.rgb;
inputPixelColor.a = inputPixelColor.a + feedbackPixelColor.a * feedbackLevel;
}
gl_FragColor = inputPixelColor;
}