Hi my friends,
I am trying to convert this ShaderToy shader (link to Shadertoy shader) to the Processing, but the processing seems to have different color channel, as shown below:
In Shadertoy shader by using this section:
// Ultra simple version, minus the window dressing.
void mainImage(out vec4 fragColor, in vec2 fragCoord){
fragColor = 1. - texture(iChannel0, fragCoord/iResolution.xy).wyyw;
}
It results black pattern and greenish background.
But in the Processing shader, using the same line of code it will get a grey image without any pattern.
If it’s using this line:
// Ultra simple version, minus the window dressing.
void main()
{
gl_FragColor = texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).wyyw;
}
it will get red background and whitish pattern:
If it’s using this line of code:
// Ultra simple version, minus the window dressing.
void main()
{
gl_FragColor = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).zxxz;
}
it will get blue background and redish pattern:
But, interestingly, if it is using the full version of the shadertoy code, the Processing rendering result will appear approximately the same as the Shadertoy result.
So would anyone like to explain a little bit how processing’s channel thing work and its difference to the shadertoy? how to solve this issue in converting the Shadertoy shader?
Here is the simple version:
The Processing code:
PShader BufferAShader;
PShader ImageShader;
PGraphics buffer1, buffer2;
int looping = 0;
void setup()
{
size(800, 450, P3D);
buffer1 = createGraphics(width, height, P2D);
buffer1.noSmooth();
buffer1.beginDraw();
buffer1.blendMode(REPLACE);
buffer1.endDraw();
buffer2 = createGraphics(width, height, P2D);
buffer2.noSmooth();
buffer2.beginDraw();
buffer2.blendMode(REPLACE);
buffer2.endDraw();
BufferAShader = loadShader("ReactionDiffusion_BufferA.glsl");
BufferAShader.set("iResolution", float(width), float(height));
ImageShader = loadShader("ReactionDiffusion_Image.glsl");
ImageShader.set("iResolution", float(width), float(height));
}
void draw()
{
PGraphics buffTemp = null;
PGraphics buffLast = null;
int loopSize = 2;
PGraphics[] buffs = {buffer1, buffer2};
buffTemp = buffs[looping%loopSize];
buffLast = buffs[(looping+1)%loopSize];
looping++;
BufferAShader.set("iFrame", frameCount);
BufferAShader.set("iTime", millis()/1000.0);
BufferAShader.set("iChannel0", buffTemp);
buffLast.beginDraw();
buffLast.shader(BufferAShader);
buffLast.rect(0, 0, width, height);
buffLast.endDraw();
ImageShader.set("iChannel0", buffLast);
buffTemp.beginDraw();
buffTemp.shader(ImageShader);
buffTemp.rect(0, 0, width, height);
buffTemp.endDraw();
image(buffTemp, 0, 0);
}
ReactionDiffusion_BufferA.glsl:
// Reaction-diffusion pass.
//
// Here's a really short, non technical explanation:
//
// To begin, sprinkle the buffer with some initial noise on the first few frames (Sometimes, the
// first frame gets skipped, so you do a few more).
//
// During the buffer loop pass, determine the reaction diffusion value using a combination of the
// value stored in the buffer's "X" channel, and a the blurred value - stored in the "Y" channel
// (You can see how that's done in the code below). Blur the value from the "X" channel (the old
// reaction diffusion value) and store it in "Y", then store the new (reaction diffusion) value
// in "X." Display either the "X" value or "Y" buffer value in the "Image" tab, add some window
// dressing, then repeat the process. Simple... Slightly confusing when I try to explain it, but
// trust me, it's simple. :)
//
// Anyway, for a more sophisticated explanation, here are a couple of references below:
//
// Reaction-Diffusion by the Gray-Scott Model - http://www.karlsims.com/rd.html
// Reaction-Diffusion Tutorial - http://www.karlsims.com/rd.html
#ifdef GL_ES
precision highp float;
#endif
#define PROCESSING_COLOR_SHADER
uniform int iFrame;
uniform float iTime;
uniform vec2 iResolution;
uniform sampler2D iChannel0;
// Cheap vec2 to vec3 hash. Works well enough, but there are other ways.
vec3 hash33(in vec2 p)
{
float n = sin(dot(p, vec2(41.0, 289.0)));
return fract(vec3(2097151.0, 262144.0, 32768.0)*n);
}
// Serves no other purpose than to save having to write this out all the time. I could write a
// "define," but I'm pretty sure this'll be inlined.
vec4 tx(in vec2 p)
{
return texture2D(iChannel0, p);
}
// Weighted blur function. Pretty standard
float blur(in vec2 p)
{
// Used to move to adjoining pixels. - uv + vec2(-1, 1)*px, uv + vec2(1, 0)*px, etc.
vec3 e = vec3(1.0, 0.0, -1.0);
vec2 px = 1.0 / iResolution.xy;
// Weighted 3x3 blur, or a cheap and nasty Gaussian blur approximation.
float res = 0.0;
// Four corners. Those receive the least weight.
res += tx(p + e.xx*px).x + tx(p + e.xz*px).x + tx(p + e.zx*px).x + tx(p + e.zz*px).x;
// Four sides, which are given a little more weight.
res += (tx(p + e.xy*px).x + tx(p + e.yx*px).x + tx(p + e.yz*px).x + tx(p + e.zy*px).x)*2.0;
// The center pixel, which we're giving the most weight to, as you'd expect.
res += tx(p + e.yy*px).x*4.0;
// Normalizing.
return res/16.0;
}
// The reation diffusion loop.
//
void main()
{
vec2 uv = gl_FragCoord.xy/iResolution.xy; // Screen coordinates. Range: [0, 1]
vec2 pw = 1.0/iResolution.xy; // Relative pixel width. Used for neightboring pixels, etc.
// The blurred pixel. This is the result that's used in the "Image" tab. It's also reused
// in the next frame in the reaction diffusion process (see below).
float avgReactDiff = blur(uv);
// The noise value. Because the result is blurred, we can get away with plain old static noise.
// However, smooth noise, and various kinds of noise textures will work, too.
vec3 noise = hash33(uv + vec2(53.0, 43.0)*iTime)*0.6 + 0.2;
// Used to move to adjoining pixels. - uv + vec2(-1, 1)*px, uv + vec2(1, 0)*px, etc.
vec3 e = vec3(1.0, 0.0, -1.0);
// Gradient epsilon value. The "1.5" figure was trial and error, but was based on the 3x3 blur radius.
vec2 pwr = pw*1.5;
// Use the blurred pixels (stored in the Y-Channel) to obtain the gradient. I haven't put too much
// thought into this, but the gradient of a pixel on a blurred pixel grid (average neighbors), would
// be analogous to a Laplacian operator on a 2D discreet grid. Laplacians tend to be used to describe
// chemical flow, so... Sounds good, anyway. :)
//
// Seriously, though, take a look at the formula for the reacion-diffusion process, and you'll see
// that the following few lines are simply putting it into effect.
// Gradient of the blurred pixels from the previous frame.
vec2 lap = vec2(tx(uv + e.xy*pwr).y - tx(uv - e.xy*pwr).y, tx(uv + e.yx*pwr).y - tx(uv - e.yx*pwr).y);
// Add some diffusive expansion, scaled down to the order of a pixel width.
uv = uv + lap*pw*3.0;
// Stochastic decay. Ie: A differention equation, influenced by noise.
// You need the decay, otherwise things would keep increasing, which in this case means a white screen
float newReactDiff = tx(uv).x + (noise.z - 0.5)*0.0025 - 0.002;
// Reaction-diffusion.
newReactDiff += dot(tx(uv + (noise.xy - 0.5)*pw).xy, vec2(1.0, -1.0))*0.145;
// Storing the reaction diffusion value in the X channel, and avgReactDiff (the blurred pixel value)
// in the Y channel. However, for the first few frames, we add some noise. Normally, one frame would
// be enough, but for some weird reason, it doesn't always get stored on the very first frame.
if(iFrame>9) gl_FragColor.xy = clamp(vec2(newReactDiff, avgReactDiff/0.98), 0.0, 1.0);
else gl_FragColor = vec4(noise, 1.0);
}
ReactionDiffusion_Image.glsl:
/*
Reaction Diffusion - 2 Pass
---------------------------
Simple 2 pass reaction-diffusion, based off of "Flexi's" reaction-diffusion examples.
It takes about ten seconds to reach an equilibrium of sorts, and in the order of a
minute longer for the colors to really settle in.
I'm really thankful for the examples Flexi has been putting up lately. From what I
understand, he's used to showing his work to a lot more people on much bigger screens,
so his code's pretty reliable. Reaction-diffusion examples are temperamental. Change
one figure by a minute fraction, and your image can disappear. That's why it was really
nice to have a working example to refer to.
Anyway, I've done things a little differently, but in essense, this is just a rehash
of Flexi's "Expansive Reaction-Diffusion" example. I've stripped this one down to the
basics, so hopefully, it'll be a little easier to take in than the multitab version.
There are no outside textures, and everything is stored in the A-Buffer. I was
originally going to simplify things even more and do a plain old, greyscale version,
but figured I'd better at least try to pretty it up, so I added color and some very
basic highlighting. I'll put up a more sophisticated version at a later date.
By the way, for anyone who doesn't want to be weighed down with extras, I've provided
a simpler "Image" tab version below.
One more thing. Even though I consider it conceptually impossible, it wouldn't surprise
me at all if someone, like Fabrice, produces a single pass, two tweet version. :)
Based on:
// Gorgeous, more sophisticated example:
Expansive Reaction-Diffusion - Flexi
https://www.shadertoy.com/view/4dcGW2
// A different kind of diffusion example. Really cool.
Gray-Scott diffusion - knighty
https://www.shadertoy.com/view/MdVGRh
*/
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
#define PROCESSING_COLOR_SHADER
uniform vec2 iResolution;
uniform sampler2D iChannel0;
// Ultra simple version, minus the window dressing.
void main()
{
//gl_FragColor = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).wyyw;
//gl_FragColor = texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).wyyw;
gl_FragColor = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).zxxz;
}
Here is the full version, if anyone would like to try. BTW, I’m using the Processing 3.5
The Processing code:
PShader BufferAShader;
PShader ImageShader;
PGraphics buffer1, buffer2;
int looping = 0;
void setup()
{
size(800, 450, P3D);
buffer1 = createGraphics(width, height, P2D);
buffer1.noSmooth();
buffer1.beginDraw();
buffer1.blendMode(REPLACE);
buffer1.endDraw();
buffer2 = createGraphics(width, height, P2D);
buffer2.noSmooth();
buffer2.beginDraw();
buffer2.blendMode(REPLACE);
buffer2.endDraw();
BufferAShader = loadShader("ReactionDiffusion_BufferA.glsl");
BufferAShader.set("iResolution", float(width), float(height));
ImageShader = loadShader("ReactionDiffusion_Image.glsl");
ImageShader.set("iResolution", float(width), float(height));
}
void draw()
{
PGraphics buffTemp = null;
PGraphics buffLast = null;
int loopSize = 2;
PGraphics[] buffs = {buffer1, buffer2};
buffTemp = buffs[looping%loopSize];
buffLast = buffs[(looping+1)%loopSize];
looping++;
BufferAShader.set("iFrame", frameCount);
BufferAShader.set("iTime", millis()/1000.0);
BufferAShader.set("iChannel0", buffTemp);
buffLast.beginDraw();
buffLast.shader(BufferAShader);
buffLast.rect(0, 0, width, height);
buffLast.endDraw();
//image(buffLast, 0, 0);
ImageShader.set("iTime", millis()/1000.0); // warm color version uniform float variables
ImageShader.set("iChannel0", buffLast);
buffTemp.beginDraw();
buffTemp.shader(ImageShader);
buffTemp.rect(0, 0, width, height);
buffTemp.endDraw();
image(buffTemp, 0, 0);
}
ReactionDiffusion_BufferA.glsl:
// Reaction-diffusion pass.
//
// Here's a really short, non technical explanation:
//
// To begin, sprinkle the buffer with some initial noise on the first few frames (Sometimes, the
// first frame gets skipped, so you do a few more).
//
// During the buffer loop pass, determine the reaction diffusion value using a combination of the
// value stored in the buffer's "X" channel, and a the blurred value - stored in the "Y" channel
// (You can see how that's done in the code below). Blur the value from the "X" channel (the old
// reaction diffusion value) and store it in "Y", then store the new (reaction diffusion) value
// in "X." Display either the "X" value or "Y" buffer value in the "Image" tab, add some window
// dressing, then repeat the process. Simple... Slightly confusing when I try to explain it, but
// trust me, it's simple. :)
//
// Anyway, for a more sophisticated explanation, here are a couple of references below:
//
// Reaction-Diffusion by the Gray-Scott Model - http://www.karlsims.com/rd.html
// Reaction-Diffusion Tutorial - http://www.karlsims.com/rd.html
#ifdef GL_ES
precision highp float;
#endif
#define PROCESSING_COLOR_SHADER
uniform int iFrame;
uniform float iTime;
uniform vec2 iResolution;
uniform sampler2D iChannel0;
// Cheap vec2 to vec3 hash. Works well enough, but there are other ways.
vec3 hash33(in vec2 p)
{
float n = sin(dot(p, vec2(41.0, 289.0)));
return fract(vec3(2097151.0, 262144.0, 32768.0)*n);
}
// Serves no other purpose than to save having to write this out all the time. I could write a
// "define," but I'm pretty sure this'll be inlined.
vec4 tx(in vec2 p)
{
return texture2D(iChannel0, p);
}
// Weighted blur function. Pretty standard
float blur(in vec2 p)
{
// Used to move to adjoining pixels. - uv + vec2(-1, 1)*px, uv + vec2(1, 0)*px, etc.
vec3 e = vec3(1.0, 0.0, -1.0);
vec2 px = 1.0 / iResolution.xy;
// Weighted 3x3 blur, or a cheap and nasty Gaussian blur approximation.
float res = 0.0;
// Four corners. Those receive the least weight.
res += tx(p + e.xx*px).x + tx(p + e.xz*px).x + tx(p + e.zx*px).x + tx(p + e.zz*px).x;
// Four sides, which are given a little more weight.
res += (tx(p + e.xy*px).x + tx(p + e.yx*px).x + tx(p + e.yz*px).x + tx(p + e.zy*px).x)*2.0;
// The center pixel, which we're giving the most weight to, as you'd expect.
res += tx(p + e.yy*px).x*4.0;
// Normalizing.
return res/16.0;
}
// The reation diffusion loop.
//
void main()
{
vec2 uv = gl_FragCoord.xy/iResolution.xy; // Screen coordinates. Range: [0, 1]
vec2 pw = 1.0/iResolution.xy; // Relative pixel width. Used for neightboring pixels, etc.
// The blurred pixel. This is the result that's used in the "Image" tab. It's also reused
// in the next frame in the reaction diffusion process (see below).
float avgReactDiff = blur(uv);
// The noise value. Because the result is blurred, we can get away with plain old static noise.
// However, smooth noise, and various kinds of noise textures will work, too.
vec3 noise = hash33(uv + vec2(53.0, 43.0)*iTime)*0.6 + 0.2;
// Used to move to adjoining pixels. - uv + vec2(-1, 1)*px, uv + vec2(1, 0)*px, etc.
vec3 e = vec3(1.0, 0.0, -1.0);
// Gradient epsilon value. The "1.5" figure was trial and error, but was based on the 3x3 blur radius.
vec2 pwr = pw*1.5;
// Use the blurred pixels (stored in the Y-Channel) to obtain the gradient. I haven't put too much
// thought into this, but the gradient of a pixel on a blurred pixel grid (average neighbors), would
// be analogous to a Laplacian operator on a 2D discreet grid. Laplacians tend to be used to describe
// chemical flow, so... Sounds good, anyway. :)
//
// Seriously, though, take a look at the formula for the reacion-diffusion process, and you'll see
// that the following few lines are simply putting it into effect.
// Gradient of the blurred pixels from the previous frame.
vec2 lap = vec2(tx(uv + e.xy*pwr).y - tx(uv - e.xy*pwr).y, tx(uv + e.yx*pwr).y - tx(uv - e.yx*pwr).y);
// Add some diffusive expansion, scaled down to the order of a pixel width.
uv = uv + lap*pw*3.0;
// Stochastic decay. Ie: A differention equation, influenced by noise.
// You need the decay, otherwise things would keep increasing, which in this case means a white screen
float newReactDiff = tx(uv).x + (noise.z - 0.5)*0.0025 - 0.002;
// Reaction-diffusion.
newReactDiff += dot(tx(uv + (noise.xy - 0.5)*pw).xy, vec2(1.0, -1.0))*0.145;
// Storing the reaction diffusion value in the X channel, and avgReactDiff (the blurred pixel value)
// in the Y channel. However, for the first few frames, we add some noise. Normally, one frame would
// be enough, but for some weird reason, it doesn't always get stored on the very first frame.
if(iFrame>9) gl_FragColor.xy = clamp(vec2(newReactDiff, avgReactDiff/0.98), 0.0, 1.0);
else gl_FragColor = vec4(noise, 1.0);
}
ReactionDiffusion_Image.glsl:
/*
Reaction Diffusion - 2 Pass
---------------------------
Simple 2 pass reaction-diffusion, based off of "Flexi's" reaction-diffusion examples.
It takes about ten seconds to reach an equilibrium of sorts, and in the order of a
minute longer for the colors to really settle in.
I'm really thankful for the examples Flexi has been putting up lately. From what I
understand, he's used to showing his work to a lot more people on much bigger screens,
so his code's pretty reliable. Reaction-diffusion examples are temperamental. Change
one figure by a minute fraction, and your image can disappear. That's why it was really
nice to have a working example to refer to.
Anyway, I've done things a little differently, but in essense, this is just a rehash
of Flexi's "Expansive Reaction-Diffusion" example. I've stripped this one down to the
basics, so hopefully, it'll be a little easier to take in than the multitab version.
There are no outside textures, and everything is stored in the A-Buffer. I was
originally going to simplify things even more and do a plain old, greyscale version,
but figured I'd better at least try to pretty it up, so I added color and some very
basic highlighting. I'll put up a more sophisticated version at a later date.
By the way, for anyone who doesn't want to be weighed down with extras, I've provided
a simpler "Image" tab version below.
One more thing. Even though I consider it conceptually impossible, it wouldn't surprise
me at all if someone, like Fabrice, produces a single pass, two tweet version. :)
Based on:
// Gorgeous, more sophisticated example:
Expansive Reaction-Diffusion - Flexi
https://www.shadertoy.com/view/4dcGW2
// A different kind of diffusion example. Really cool.
Gray-Scott diffusion - knighty
https://www.shadertoy.com/view/MdVGRh
*/
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
#define PROCESSING_COLOR_SHADER
uniform vec2 iResolution;
uniform sampler2D iChannel0;
/*
// Ultra simple version, minus the window dressing.
void main()
{
//gl_FragColor = texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).wyyw;
gl_FragColor = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).zxxz;
}
*/
// warm color version
uniform float iTime;
void main()
{
// screen coordinates
vec2 uv = gl_FragCoord.xy / iResolution.xy;
// Read in the blurred pixel value. There's no rule that says you can't read in the
// value in the "X" channel, but blurred stuff is easier to bump, that's all.
float c = 1. - texture2D(iChannel0, uv).y;
// Reading in the same at a slightly offsetted position. The difference between
// "c2" and "c" is used to provide the highlighting.
float c2 = 1.0 - texture(iChannel0, uv + 0.5 / iResolution.xy).y;
// Color the pixel by mixing two colors in a sinusoidal kind of pattern.
//
float pattern = -cos(uv.x*0.75*3.14159 - 0.9)*cos(uv.y*1.5*3.14159 - 0.75)*0.5 + 0.5;
//
// Blue and gold, for an abstract sky over a... wheat field look. Very artsy. :)
vec3 col = pow(vec3(1.5, 1.0, 1.0)*c, vec3(1.0, 2.25, 6.0));
col = mix(col, col.zyx, clamp(pattern - 0.2, 0.0, 1.0));
// Extra color variations.
//vec3 col = mix(vec3(c*1.2, pow(c, 8.0), pow(c, 2.0)), vec3(c*1.3, pow(c, 2.0), pow(c, 10.0)), pattern);
//vec3 col = mix(vec3(c*1.3, c*c, pow(c, 10.0)), vec3(c*c*c, c*sqrt(c), c), pattern);
// Adding the highlighting. Not as nice as proper bump mapping, but still pretty effective.
col += vec3(0.6, 0.85, 1.0)*max(c2*c2 - c*c, 0.0)*12.0;
// Apply a vignette and increase the brightness for that fake spotlight effect.
col *= pow(16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y), 0.125)*1.15;
// Fade in for the first few seconds.
col *= smoothstep(0.0, 1.0, iTime/2.0);
// Done... Edit: Final values should be gamma corrected, unless you're deliberately
// not doing it for stylistic purposes... In this case, I forgot, but let's just pretend
// it's a postprocessing effect. :D
gl_FragColor = vec4(min(col, 1.0), 1.0);
}