diff --git a/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde b/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde new file mode 100644 index 000000000..c6fb40b1d --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/CustomBlend.pde @@ -0,0 +1,110 @@ +/** + * Custom Blend + * + * The OpenGL-based renderers (P2D and P3D) only support some of the + * blending modes available in the default renderer. The reason for this + * is that the blend equations in OpenGL allow for combinations of the + * form dest_factor * dest_color + src_factor * src_color of the source and + * destination colors (see this page http://www.opengl.org/wiki/Blending + * for an extensive discussion of blending in OpenGL). + * Complex blending modes typically available in photo editing tools, + * like hard light or dodge, cannot be modeled with those equations. + * However, we can implement virtually any blending math directly in the + * fragment shader. + * + * This example shows how custom blend shaders can be loaded and used in + * Processing. + * For detailed information on how to implement Photoshop-like blending modes, + * check the following pages (a bit old but still useful): + * http://www.pegtop.net/delphi/articles/blendmodes/index.htm + * http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/ + * + */ + +PImage destImage; +PImage srcImage; +PShader dodge; +PShader burn; +PShader overlay; +PShader difference; + +void setup() { + size(640, 360, P2D); + destImage = loadImage("leaves.jpg"); + srcImage = loadImage("moonwalk.jpg"); + + initShaders(); +} + +void draw() { + background(0); + + shader(dodge); + drawOutput(0, 0, width/2, height/2); + shader(burn); + drawOutput(width/2, 0, width/2, height/2); + shader(overlay); + drawOutput(0, height/2, width/2, height/2); + shader(difference); + drawOutput(width/2, height/2, width/2, height/2); + + noLoop(); +} + +void initShaders() { + dodge = loadShader("dodge.glsl"); + burn = loadShader("burn.glsl"); + overlay = loadShader("overlay.glsl"); + difference = loadShader("difference.glsl"); + + // The names destination and source come from the OpenGL terminology: + // destination from the image already in the framebuffer, or "base layer", + // and source for the image that will be blended into the framebuffer, or + // "blend layer": + dodge.set("destSampler", destImage); + dodge.set("srcSampler", srcImage); + burn.set("destSampler", destImage); + burn.set("srcSampler", srcImage); + overlay.set("destSampler", destImage); + overlay.set("srcSampler", srcImage); + difference.set("destSampler", destImage); + difference.set("srcSampler", srcImage); + + // We set the sizes of de st and src images, and the rectangular areas + // from the images that we will use for blending: + dodge.set("destSize", 640, 360); + dodge.set("destRect", 100, 50, 200, 200); + burn.set("destSize", 640, 360); + burn.set("destRect", 100, 50, 200, 200); + overlay.set("destSize", 640, 360); + overlay.set("destRect", 100, 50, 200, 200); + difference.set("destSize", 640, 360); + difference.set("destRect", 100, 50, 200, 200); + + dodge.set("srcSize", 640, 360); + dodge.set("srcRect", 0, 0, 640, 360); + burn.set("srcSize", 640, 360); + burn.set("srcRect", 0, 0, 640, 360); + overlay.set("srcSize", 640, 360); + overlay.set("srcRect", 0, 0, 640, 360); + difference.set("srcSize", 640, 360); + difference.set("srcRect", 0, 0, 640, 360); +} + +void drawOutput(float x, float y, float w, float h) { + pushMatrix(); + translate(x, y); + noStroke(); + beginShape(QUAD); + // Although we are not associating a texture to + // this shape, the uv coordinates will be stored + // anyways so they can be used in the fragment + // shader to access the destination and source + // images. + vertex(0, 0, 0, 0); + vertex(w, 0, 1, 0); + vertex(w, h, 1, 1); + vertex(0, h, 0, 1); + endShape(); + popMatrix(); +} diff --git a/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl b/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl new file mode 100644 index 000000000..0d090a6ac --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/burn.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(1.0 - (1.0 - destColor) / srcColor, 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl b/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl new file mode 100644 index 000000000..7f0040a22 --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/difference.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(abs(srcColor - destColor), 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl b/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl new file mode 100644 index 000000000..115dbaa09 --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/dodge.glsl @@ -0,0 +1,22 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + gl_FragColor = vec4(destColor / (1.0 - srcColor), 1.0); +} \ No newline at end of file diff --git a/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl b/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl new file mode 100644 index 000000000..f7d28efac --- /dev/null +++ b/java/examples/Topics/Shaders/CustomBlend/data/overlay.glsl @@ -0,0 +1,28 @@ +uniform sampler2D destSampler; +uniform sampler2D srcSampler; + +uniform ivec2 destSize; +uniform ivec4 destRect; + +uniform ivec2 srcSize; +uniform ivec4 srcRect; + +varying vec4 vertTexCoord; + +void main() { + vec2 st = vertTexCoord.st; + + vec2 dest = vec2(destRect.xy) / vec2(destSize) + st * vec2(destRect.zw) / vec2(destSize); + vec2 src = vec2(srcRect.xy) / vec2(srcSize) + st * vec2(srcRect.zw) / vec2(srcSize); + + vec3 destColor = texture2D(destSampler, dest).rgb; + vec3 srcColor = texture2D(srcSampler, src).rgb; + + float luminance = dot(vec3(0.2126, 0.7152, 0.0722), destColor); + + if (luminance < 0.5) { + gl_FragColor = vec4(2.0 * destColor * srcColor, 1.0); + } else { + gl_FragColor = vec4(1.0 - 2.0 * (1.0 - destColor) * (1.0 - srcColor), 1); + } +} \ No newline at end of file