Search code examples
gpushaderwebgl

Shadertoy: How to mix-add-multiply any two shaders?


Are there any crafty tricks to mix two shaders together? i.e. to add the shaders together, else to render one shader in a central square and another as a frame? Can we rename some of the input-output parameters and add them in a final image mix meta-function?

i.e. If i change

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{...}

changed to:

void mainImage( out vec4 A_FragColor, in vec2 fragCoord )
{...}

and

void mainImage( out vec4 B_FragColor, in vec2 fragCoord )
{...}

Perhaps I can mix and lerp the shaders A and B from left to right of the canvas?


Solution

  • You can use the render to texture enabled by Shadertoy buffer tabs and iChannel mapping.

    To do it, just add a BufA tab and put your first shader code into it, then do the same with BufB tab and your second shader code.

    For example I will generate two gradient images and sum them. BufA will draw some red to black, and BufB will draw some black to green gradient.

    // Shader in buffer A returns a red to black gradient
    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        vec2 uv = fragCoord / iResolution.xy;
        fragColor = vec4(uv.x, 0., 0., 1.);
    }
    
    // Shader in buffer B returns a black to green gradient
    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        vec2 uv = fragCoord / iResolution.xy;
        fragColor = vec4(0., 1. - uv.x, 0., 1.);
    }
    

    Enable iChannel0 and iChannel1 in the bottom panel and connect them to BufA and BufB. At this point ShaderToy will render both shaders into iChannel textures, before Image tab main shader is called.

    In the Image tab, you can retrieve the textures by accessing iChannel[i] you mapped, and use iChannelResolution[i] to compute uv coordinates.

    And you can mix everything the way you want

    void mainImage( out vec4 fragColor, in vec2 fragCoord )
    {
        // Normalized pixel coordinates (from 0 to 1)
        vec2 uvA = fragCoord / iChannelResolution[0].xy;
        vec2 uvB = fragCoord / iChannelResolution[1].xy;
    
        // Output to screen
        // Fragment is the sum of both gradients: red to green
        fragColor = texture(iChannel0, uvA) + texture(iChannel1, uvB);
    }
    

    The whole thing should look like this

    enter image description here