How to converting ShaderToy shader color channel to Processing correctly

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 :slight_smile:
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);
  

}

Hi @psamead,

hmmm! Maybe I didn’t understood your question correct, but if you’re using different colors, it is obvious it has different output !?

texture return a vec4 which represents a color format rgba.
The single elements can be referred by r,g,b,a or x,y,z,w.

Original ShaderToy you posted

sets the returned color from inverted (1.-) agga (alpha,green,green,alpha) as rgba

and that is different to

sets the returned color from inverted (1.-) brrb (blue,red,red,blue) as rgba

And if you are using the first one from ShaderToy it delivers nearly the same result as in ShaderToy …

Cheers
— mnse

1 Like

Hi mnse,
Thanks for your kind reply. Your reply is very informative and inspiring, much appreciated. :slight_smile: It got me to consider the actual channels things. I am using the version 3.5.4. of the Processing, this could be the main issue. I have not yet tested any other versions. This issue may not occur in other versions.

The blank display seems to come from vec4 color output which it is set to 0.0. There are several posts mention this issue here and the alpha value needs to be 1.0 to get display correctly.

The ShaderToy code

In the Processing 3.5.4 environment

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;
  
  float g = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).y;
  vec3 col = vec3(0.0, g, g);
  gl_FragColor = vec4(col, 0.0);
}

with the alpha channel set to 0.0, it will result a blank window.

Since the shadertoy result does not seem to have red color, so the red channel sets to 0.0. It seems the alpha channel has to set to 1.0 to get the correct display.

  float g = 1.0 - texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy).y;
  vec3 col = vec3(0.0, g, g);
  gl_FragColor = vec4(col, 1.0);

So I am not sure if this channel issue is only this version 3.5.4 has or the issue comes from the open gl processing using or shadertoy use a slightly different system. Would any friend like to give some information on this? Sorry for if I use any terms not correctly.