I'm creating a 2D top-down tiled game in pure Java and by now I'm trying to implement a way to do lighting.
First, some details on how I render: there is a screen class which handles all the rendering. Pixels are put inside a int[width * height]
array which is drawn on the actual screen using a BufferedImage
and Graphics.drawImage()
.
The way I am doing lighting right now is by creating a second int[width * height]
array which holds all the brightness that should be subtracted from the actual pixels. I try to use only floats ranging from 0f
to 1f
to represent RGB intensities.
So, as an example, there is the ambient lighting which is 0.3f. What I do is a pixel to pixel operation, where for each pixel that will be drawn, its RGB values are decomposed, decreased 70% (1f - 0.3f), recomposed back into a single int and stored inside the secondary int[width * height]
array. When transferring the pixels to the BufferedImage, I subtract the corresponding value inside the secondary array and get a darker image, as expected.
That was an example of darkening the image and it works fine, the problem appears when actually lighting them up: I gave the player a lightRadius and 3 light color component floats (RGB). While rendering the player, I also render it's lighting inside the secondary array by doing the following:
int mask = subtractMask[xx + yy * width];
int mr = (mask >> 16) & 255;
int mg = (mask >> 8) & 255;
int mb = mask & 255;
mr -= (int) (dist * light.r * mr);
mg -= (int) (dist * light.g * mg);
mb -= (int) (dist * light.b * mb);
subtractMask[xx + yy * width] = mr << 16 | mg << 8 | mb;
Where dist is a number ranging from 0f
to 1f
which makes the light fade with the distance from the source and light.r/g/b
are each of the components of the light that comes from the player.
What this piece of code does is basically to "give a little bit of the light that was taken out of a pixel back to it". As you can see, mr, mg and mb are made smaller (-=) and then composed back inside the number that will be subtracted from the corresponding pixel, thus making the result of the subtraction bigger (lighter).
For white light I get the desired result:
https://dl.dropbox.com/u/68532511/light1.png
The problem is when I only use the blue component (r = 0f, g = 0f, b = 1f), getting this: https://dl.dropbox.com/u/68532511/light2.png
Of course this can be explained by the fact that the tiles have very little blue values, making the ambient light take a little part of it away and making the lighting give this little part of it back (as you can see the biggest effect is on the player's pants, which are blue). What I still couldn't think of is a way to make lighting independent of how much brightness was taken away from the pixels and something that simply looks good in any way I combine the components (the result I expected for the second image was luminance like in the first one, but colored blue). So how can I implement such an effect?
Also, for making the lighting circular, I simply compute something light x^2 + y^2 <= radius^2, where if this sentence is true, the pixel should light up, executing the code I've showed before. Is there a faster way to compute this? (I lost almost 50 FPS on this effect).