Search code examples
javascriptjavashaderp5.jsfade

Fading between shadertoy shaders in p5.js?


I am trying to fade between several different types of shaders from shadertoy by importing them to p5.js and am trying to figure out the best way to do that? I am trying to figure out if I can somehow just use the tint() function to adjust the opacity of the shader after I load them in and loop through them, but don't exactly know if that's possible. Here's what I have so far, any help would be greatly appreciated.

Here is the full link to my project at https://glitch.com/~outgoing-island-enthusiasm

    let theShader;
    let oldTime;
    let shaderNdx = 0;

    const shaders = [];

    function preload(){
      // load the shaders
      shaders.push(loadShader('shader1.vert', 'shader1.frag'));
      shaders.push(loadShader('shader2.vert', 'shader2.frag'));
      shaders.push(loadShader('shader3.vert', 'shader3.frag'));
      theShader = shaders[0];  // start with the first shader
    }

    function setup() {
      //creates canvas
      createCanvas(windowWidth, windowHeight, WEBGL);
      noStroke();
    }

    function draw() {  
      // switch shaders every second
      let time = performance.now() / 1000 | 0;  // convert to seconds
      if (oldTime !== time) {
        oldTime = time;
        // increment shader index to the next shader but wrap around 
        // back to 0 at then of the array of shaders
        shaderNdx = (shaderNdx + 2) % shaders.length;
        theShader = shaders[shaderNdx]
      
      }

      //sets the active shader
      shader(theShader);

      theShader.setUniform("iResolution", [width, height]);
      theShader.setUniform("iFrame", frameCount);
      theShader.setUniform("iMouse", [mouseX, map(mouseY, 0, height, height, 0)]);
      theShader.setUniform("iTime", millis() / 1000.0);
      theShader.setUniform("u_resolution", [width, height]);
      theShader.setUniform("u_time", millis() / 1000.0);
      theShader.setUniform("u_mouse", [mouseX, map(mouseY, 0, height, height, 0)]);

      // rect gives us some geometry on the screen
      rect(0,0,width, height);
    }

    function windowResized(){
      resizeCanvas(windowWidth, windowHeight);
    }


Solution

  • Edit: The glitch.com sketch linked below now smoothly transitions between three shaders. There are always 2 active shaders (called foregroundShader and backgroundShader). Every changeEvery frames, the current foregroundShader becomes the new backgroundShader and a new shader is chosen for the foregroundShader. The backgroundShader always has alpha of 1.0 (not transparent), whereas the alpha value of the foreground shader linearly increases from 0.0 to 1.0 during the course of one "episode", using the p5.js map() function as follows:

    foregroundShader.setUniform(
        "backgroundAlpha",
        map(frameCount % changeEvery, 0, changeEvery-1, 0.0, 1.0)
    );
    

    Original answer: A possible solution for smooth transitions between two sketches (I used shaders 1 and 2 from your example in the glitch sketch here):

    Render both shaders on top of each other and give the one on the top a variable alpha (transparency) value, e.g.,

    uniform float backgroundAlpha;
    
    ...
    
    void main() {
        // Calculate r g b values of your colors somehow
        vec3 color = ...
    
        // Concatenate the alpha (transparency value)
        gl_FragColor = vec4(color.rgb, backgroundAlpha);
    }
    

    You can then set the value of backgroundAlpha from your sketch.js to some VALUE using theShader.setUniform('backgroundAlpha', VALUE), where VALUE could be the output of any function (should be between 0 and 1).

    The same principle should work for three shaders as well.

    Note that this first did not work using your glitch sketch because you used an old version of p5.js, that still had a transparency bug. It worked, however, using 1.0.0 (see changes in the index.html file).