Recently, I have implemented two fragment shaders for interactive Poisson Blending (seamless blending). 

Here is my real-time demo and code on ShaderToy:

Press 1 for normal mode, press 2 for mixed gradients, press space to clear the canvas.

Technical Brief

I followed a simplified routine of the Poisson Image Editing paper [P. Pérez, M. Gangnet, A. Blake. Poisson image editing. ACM Transactions on Graphics (SIGGRAPH’03)]:

  • Let’s name the bottom image as “BASE”, and the image to overlay as “SRC”, the final image as “RESULT”, then the color in the boundary should be the same
    • RESULT(u, v) = BASE(u, v) ∀(u, v) ∈ ∂ SRC   
  • Inside the mask region, just add the gradient of “SRC” (or the bigger gradient of “SRC” and “BASE”, if we want to mix the gradients) into the current result image:
    • RESULT(u, v) = RESULT(u, v) + ∇ SRC(u, v);
    • alternatively, 
    • RESULT(u, v) = RESULT(u, v) + max{ ∇ SRC(u, v), ∇ BASE(u, v) );

We can use jump flood algorithm to speed this procedure up: 

Implementation Details

I used one frame buffer to store the drawing of the user. 

  • iChannel0 stores the frame buffer itself
  • iChannel1 stores the keyboard response for interaction

This is a very simple drawing shader which can also be adapted for other drawing applications.

Update on 4/5/2017

It’s best to stop the Poisson blending when the delta modification abs(before – after) is small enough, or after a certain amount of iterations.

We can use the mask buffer to store the times of iterations in the second channel or rgb. 

The second frame buffer to used to iterate the Poisson blending process:

  • iChannel0 stores the previous frame buffer itself
  • iChannel1 stores the mask buffer
  • iChannel2 stores the base image
  • iChannel3 stores the source image to blend

Sometimes, the texture is not loaded in the first few frames in ShaderToy, so I sample the last pixel of this framebuffer to test whether it is initialized with correct image.

The main fragment shader is for showing the result:

  • iChannel0 stores the previous frame buffer
Ideally, the iteration should stop once the change of the colors is small enough; however, I do not want to use another pixel to store this global error variable. So just 200 iterations will induce beautiful blended image.

Here is another result:


Finally, here is a mysterious result with wrong mixing of gradients 🙂