I've been digging around for a bloom solution and many simply required shaders. None of the processing GL shader solutions I've been trying out have worked so far (need to ask Creighton / Alexander and friends), so I dug even more for a non-shader bloom technique.
This method is quite simple and involves a brightpass and a box blur. References posted below as comments.
I've made it tunable so it can render quickly, or draw nicely for your renders.
There's a few more things you can tune as well.
Adding a tint(amount); before you draw pg will allow you to control the bloom strength.
Controlling the 150 constant in this line
buffer.pixels[i] = red(buffer.pixels[i])>150 ? 255:0;
Will allow you to set a threshold for the bloom.
It's also possible to use this without additive blending, and use other ways of exposing it (drawing underneath your objects for example).
Some more notes:
- you can probably omit the updatepixels and speed things up, it's unnecessary...
- loadPixels is a bottleneck, not sure how I could speed this up (it eats bout 15ms of processing time on my machine)
Code:
// bloom code found at http://everyware.cc/files/codes/Bloom/ by EveryWare creative computing group
// modified and optimized by Flux http://www.ghost-hack.com
import processing.opengl.*;
import javax.media.opengl.*;
GL gl;
// offscreen buffer to draw the bloom onto
PGraphics pg;
// blur passes, the higher the number the more blurry the bloom becomes
int pass = 3;
// the lower the number the higher quality the bloom (less flickering)
int bufferScale = 6;
void setup() {
size(640, 480, OPENGL);
gl=((PGraphicsOpenGL)g).gl;
pg = createGraphics(width/bufferScale, height/bufferScale, P3D);
}
void draw() {
// draw renders normally
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
background(0);
stroke(255);
fill(255);
rectMode(CENTER);
ellipse(mouseX, mouseY, 50,50);
// dump contents into pg's pixel buffer
pg.beginDraw();
pg.copy(g,0, 0, width, height, 0, 0, pg.width, pg.height);
pg.endDraw();
// apply brightness pass
brightPass(pg);
// blur the brightness pass
for(int i=0; i<pass; i++)
blur(pg);
// display the brightpass as an additive blend
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
pushMatrix();
image(pg, 0, 0, width, height);
popMatrix();
}
// brightpass utils
void brightPass(PGraphics buffer) {
for(int i=0; i<buffer.pixels.length; i++)
buffer.pixels[i] = red(buffer.pixels[i])>150 ? 255:0;
}
float[][] kernel = {
{
1.0, 1.0, 1.0, 1.0, 1.0 }
,
{
1.0, 1.0, 1.0, 1.0, 1.0 }
,
{
1.0, 1.0, 1.0, 1.0, 1.0 }
,
{
1.0, 1.0, 1.0, 1.0, 1.0 }
,
{
1.0, 1.0, 1.0, 1.0, 1.0 }
};
void blur(PGraphics buffer) {
int n2 = 5/2;
int m2 = 5/2;
int[][] output = new int[buffer.width][buffer.height];
// Convolve the image
for(int y=0; y<buffer.height; y++) {
for(int x=0; x<buffer.width; x++) {
float sum = 0;
for(int k=-n2; k<=n2; k++) {
for(int j=-m2; j<=m2; j++) {
// Reflect x-j to not exceed array boundary
int xp = x-j;
int yp = y-k;
if (xp < 0) {
continue;
}
else if (x-j >= buffer.width) {
continue;
}
// Reflect y-k to not exceed array boundary
if (yp < 0) {
continue;
}
else if (yp >= buffer.height) {
continue;
}
// avoid using get for a pinch of extra speed
sum = sum + kernel[j+m2][k+n2]/25.0 * brightness(buffer.pixels[yp*buffer.width + xp]);
// sum = sum + kernel[j+m2][k+n2]/25.0 * brightness(buffer.get(xp, yp));
}
}
output[x][y] = int(sum);
}
}
for(int i=0; i<buffer.pixels.length; i++)
buffer.pixels[i] = color(output[i%buffer.width][i/buffer.width]);
}