Hi Discourse,
I'm creating one example in Processing that tries to replicate "light painting" electronically. It's kinda like your usual painting program - clicking and dragging create lines on the screen - but by drawing several lines with some variation in position and stroke, it creates an stylish stroke that resembles "light painting" like the ones you'd do with a camera capturing the movement done by a flashlight with an open shutter.
So yeah, I'm just drawing several lines. However, to properly simulate the color changes, I'd like to use a different blend mode (ADD) on each line. That way, when two lines stack, you have a much brighter color.
It's all working well, but it's also pretty slow. Probably because I'm just a Processing beginner and I may be doing something terribly wrong. I can't tell the framerate I'm getting, but it seems to be something around 15fps on a 640x480 screen. So I'd like to know whether there's something I'm doing wrong that should be avoided or something that may make the execution faster.
In a nutshell, what I'm doing is creating several images (PGraphics instances with JAVA2D mode) and drawing on them. Then I render all images to the window - the first one using a normal blending mode and the others with ADD - "flattening" them on every frame to create the effect I want.
So, is there any other (faster) way of doing what I'm doing?
Anyway, full source follows.
Code:float currentCursorX;
float currentCursorY;
float prevX;
float prevY;
int numLayers = 4;
float amountPainted = 0;
float currentSpeed;
int quality = 1; // 1 = normal, 2 = half quality
PGraphics[] img = new PGraphics[numLayers];
float[] amplitude = new float[numLayers];
float[] radius = new float[numLayers];
int[] lineStroke = new int[numLayers];
boolean isPainting = false;
void setup() {
size(640, 480);
frameRate(30);
clearWindow();
amplitude[0] = 100;
amplitude[1] = 83;
amplitude[2] = 66;
amplitude[3] = 43;
radius[0] = 0;
radius[1] = -3;
radius[2] = 6;
radius[3] = -8;
lineStroke[0] = 3;
lineStroke[1] = 3;
lineStroke[2] = 2;
lineStroke[3] = 1;
}
void draw() {
if (isPainting) {
// Cursor position attenuation for smoothing
currentCursorX -= (currentCursorX - (mouseX/quality)) / 3;
currentCursorY -= (currentCursorY - (mouseY/quality)) / 3;
drawLines(prevX, prevY, currentCursorX, currentCursorY);
prevX = currentCursorX;
prevY = currentCursorY;
}
if (mousePressed) {
if (!isPainting) startPainting();
} else {
if (isPainting) stopPainting();
}
}
void clearWindow() {
background(0);
int i;
for (i = 0; i < numLayers; i++) {
img[i] = createGraphics(width/quality, height/quality, JAVA2D);
}
}
void startPainting() {
currentSpeed = 0;
clearWindow();
isPainting = true;
prevX = currentCursorX = mouseX/quality;
prevY = currentCursorY = mouseY/quality;
}
void stopPainting() {
isPainting = false;
}
void drawLines(float x1, float y1, float x2, float y2) {
// Draw all lines with variations
int i;
float xDist = x2 - x1;
float yDist = y2 - y1;
float lineLength = sqrt(xDist * xDist + yDist * yDist);
float newAmountPainted = amountPainted + lineLength;
float newSpeed = 1 - (lineLength / 80);
if (newSpeed < 0) newSpeed = 0;
float cs = 0.1 + currentSpeed * 1.4;
float ns = 0.1 + newSpeed * 1.5;
// Erase buffer
background(0);
// Draw the lines
for (i = 0; i < numLayers; i++) {
drawLine(img[i], x1, y1, amountPainted/amplitude[i], radius[i] * cs, x2, y2, newAmountPainted/amplitude[i], radius[i] * ns, lineStroke[i]);
}
// Render layers
image(img[0], 0, 0, width, height);
for (i = 1; i < numLayers; i++) {
blend(img[i], 0, 0, width/quality, height/quality, 0, 0, width, height, ADD);
// if (i == 3) filter(BLUR, 1);
}
amountPainted = newAmountPainted;
currentSpeed = newSpeed;
}
void drawLine(PGraphics __img, float x1, float y1, float amp1, float rad1, float x2, float y2, float amp2, float rad2, int __strokeWeight) {
float ox1 = x1 + sin(amp1) * rad1;
float oy1 = y1 + cos(amp1) * rad1;
float ox2 = x2 + sin(amp2) * rad2;
float oy2 = y2 + cos(amp2) * rad2;
__img.beginDraw();
__img.stroke(#886666);
__img.smooth();
__img.strokeWeight(__strokeWeight);
// __img.strokeCap(ROUND);
__img.line(ox1, oy1, ox2, oy2);
__img.endDraw();
}
Some notes, please read:
* I *could* use P3D instead of JAVA2D. However, it's not much faster either, and it's kind of bad since I wouldn't be able to use different stroke weights or use antialiased lines apparently. Correct me if I'm wrong.
* I *could* use a few more modes of attenuation to the cursor position and line drawing position for more smoother curves, but right now this is enough.
* I'm using a lot of floats, while I could a lot of them as ints instead, and there's some more calculations than needed. I don't think it's too massive and wouldn't have that big of an impact though.
* I know I could be using additional classes for line drawing and such, but again, this is just a prototype and having those arrays is enough for now.