# 2D Software Lighting Issues in Java

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).

• Your dropbox links are broken. – ashes999 Dec 15 '13 at 4:08

Physically, adding a colored light to a scene without any of that color will not illuminate the scene at all. Think about it in science terms: what you "see" in the real world is light reflecting off of various objects. In real-life, the light spectrum contains all colors... so the corresponding wavelength of light gets reflected off of objects and you "see" their color. However, if you restrict this spectrum, objects that absorb the corresponding colors won't reflect any back! A better way to think about this is that if you shine a pure blue light in a pure green room, you won't be able to see the walls!

That being said, I understand what you're trying to achieve in the game, and the best way to get what you're looking for is to use alpha compositing. Ideally, you can use this to generate the ambient effects that you want as well, and it will likely be faster than going through and modifying pixel values one by one.

For example, one way to darken your scene is to generate a black image with alpha 0.3, and composite it using AlphaComposite.SRC_OVER:

BufferedImage scene = ...;

int width = scene.getWidth();
int height = scene.getHeight();
int imgType = scene.getType();
BufferedImage blackImage = new BufferedImage(width, height, imgType);
Graphics2D blackImageGraphics = blackImage.createGraphics();
blackImageGraphics.setColor(new Color(0.0f, 0.0f, 0.0f, 0.3f));
blackImageGraphics.clearRect(0, 0, width, height);

Graphics2D sceneGraphics = scene.createGraphics();
sceneGraphics.setComposite(AlphaComposite.SrcOver);
sceneGraphics.drawImage(blackImage, 0, 0, width, height, null);


In order to add a colored "light" around your character, you can do a similar trick. First, render an image that's black and transparent everywhere except your "light" area, where you give a pixel value of whatever color you'd like your "light" to be, and an alpha value of, say, 0.5. Then, you can render this picture over your scene using AlphaComposite.SRC_OVER in exactly the same way as in the code above.

After you read the wikipedia article, you can find a lot of information regarding the different ways to play with alpha in Java by reading the documentation

• I tried to implement the over operator by myself but the result did not come quite as I wanted it to be... What I do is I draw a white circle (for white light) and over it with the darkened image (ambient light) using the distance from the light point as the alpha value. The problem is that I simply can't see the player or whats around it, because the middle of the circle is drawn with almost 100% alpha. If I just limit this alpha value, I become able to see the player, but not quite illuminated (exactly because now the alpha value is lower). Am I doing something wrong? – Thiago Mar 20 '13 at 5:33
• Don't scale your alpha value from 0.0 to 1.0... Make it go from something like 0.2 to 0.6. – Mokosha Mar 20 '13 at 12:41
• I actually tried it, but then the luminosity is not as strong as it should be. – Thiago Mar 20 '13 at 16:12