Lighting Woes (Part 1)

Greetings everyone! I’ve been quietly lurking on the forums for some time now, but this would be my first post. I’ve been working on implementing tile based lighting/shadows in my current project (a 2d, side-scrolling, terraria esque sandbox game), and I think I may need some help, as my current method seems to be presenting a bit of a dilemma.

My current method of rendering lighting is by using a gradient “shadow” overlay for each block, including Empty tiles:

This seems to look good, until you consider that shadows overlay the background image as well:

Obviously, I don’t want a single campfire (or in this case, radioactive water block!) illuminating the background image which represents land/sky far off in the distance. For that matter, I don’t want to have to draw shadow blocks over the background either (which dims the stars, for example).

The obvious solution would be to not draw shadows over empty blocks, but then players and objects would appear bright and unshaded at night or in dark areas.

The background has to be drawn behind everything, but the “shadows” have to be drawn over everything; yet I don’t want the shadows to affect the background image. Is there any way around this, or will I have to redesign my lighting system?

There are plenty of ways around it, but you’ll have to tell us what you’re working with. Are you using LWJGL?

What is LWJGL?

I’m finding the correct light level for each tile using my own methods, and I’m just using pure java to draw images on to a jPanel in my “SpriteSheet” class.

The layers are being drawn on top of each-other in the following order:

  1. background
  2. tiles
  3. players / mobs
  4. shadow overlays

Granted, there are a few more layers than that, but that’s the concept.

The shadow overlays are being drawn from an alpha gradient image, though I could just as easily draw them in code using the Graphics.fillRect() function.

I can post some of my code if it is relevant, but I saw this as more of a conceptual problem.

I just realized I have the same problem, so I’ll solve it with you :stuck_out_tongue:

In your lighting engine, for each update, you could tag if a block is “in the sky” (while loop starting at the top of the world, you don’t have to do the entire world at once, just what’s around the player). “In the sky” means it’s (probably) air blocks. If you could imagine taking the paint bucket tool in Paint, and filling the sky, that would be the effect.

When you’re doing your shadow rendering, if it’s “in the sky” you don’t have to render it. However, trees won’t get any light on them, so you’ll have to apply lighting to them afterwards by doing these steps:

  1. Render the tree with full brightness.
  2. For each light level, render that “part” of the tree in black and an alpha value suitable for light. This simulates a shadow.
  3. If you’re doing coloured lighting, the colour you use to tint the tree part should be a mix of black and the light colour, however you should’ve already pre-calculated the light colour.

I hope that helps! I need to go implement this too now.

Thanks for sharing!

I was considering that method, actually. I already have tags for “air blocks” and am only rendering what’s on screen, currently.

How do you shade your characters / trees, though? Are you manipulating the image in code? That might be a little beyond me. I’ve never worked with manipulating images before.

I’ll go look up some examples! But, if you come across (or come up with) a simple method that you wouldn’t mind sharing, that would be great ;D

… I just realized my entities rely on the lighting shadows I just removed (for background) for “lighting”. Oops.

Manipulating an image in real time is EXTREMELY slow. What I suggest is to render the normal image, then render the same image (or part of it) again right on top, with transparency and tint for lighting. I’ll need to solve this myself; after I do I’ll share results on what I did.

I’ve been working on other aspects of my project in the meantime, but I got to thinking…

This would not be a very memory efficient solution, but it wouldn’t bog down processing power:

How about taking each sprite sheet loaded, and parsing through that image distinguishing between transparent pixels, and non-alpha pixels to get an outline of that sprite sheet – create one outline of that sprite sheet with whatever base step in gradient you want to use, and whatever colors (so, for a basic shadow, let’s say a color of 0,0,0,32. If you want colored light, you can do one overlay for each color shade as well), and store that along with the sprite sheet. Now, if I want to shade my sprite, I just draw that image over it however many times dark I want it shaded. Or, if I don’t want to do that, and want to use yet more memory, I can load more shaded-silhouettes for each alpha level.

It would mean that larger objects, such as the trees in my OP, would need to either be broken up into smaller tiles, or would just have one light-level throughout with no differential shading.

Would this be easy enough to do? I still haven’t looked into manipulating images yet, but it doesn’t seem like it would be all that difficult. You could even draw these shadow overlays out yourself, but that would be a bit of a pain to do for every object in the game if you have a lot of objects…

Everything you just said can be done with the GPU so it’s quite efficient.

Tinting a sprite seems easy enough (I don’t know what you’re using to make your game, so I can’t really answer this yet). You would specify the colour and alpha value. No need for multiple spritesheets.

As for tinting a part of a sprite, I’m sure there’s a way to draw a “part” of a sprite.

Example code (libgdx):

batch.setColor(0, 0, 0, 0.25f); // set the batch colour to black, 1/4 transparent
batch.draw(sprite, x, y, width, height, 0.25f, 0.25f, 0.75f, 0.75f); // draw the sprite at x, y with width and height, rendering only the given area

UV in textures works like this:

Ah yes, I was aware that you’re able to draw part of an image. In fact, that’s what I’m doing for my sprite sheets already. For some reason it just didn’t cross my mind that I could use it to draw objects in a tiled manner!

I’m using Java’s Graphics / Graphics2D class to draw to my Jpanel. What does “batch” represent in your example? I’m pretty sure that setting the color of Java’s Graphics2D object doesn’t affect the color of images drawn using the draw method. So, I’m assuming libgdx is some kind of library alternative to Java’s Graphics class?

EDIT:

So, I’ve been looking up libgdx! As you might tell, I haven’t really worked with external libraries before in Java. Not really keen on switching to it for a single feature, but I guess it might be worth it in the long run if it offers more flexibility, or better performance.

17 days later…

I fixed the issue!

WARNING COMPLICATED THINGS BELOW

I rendered the world (without lighting) to a frame buffer. I rendered just the lighting “shadows” to another frame buffer. I rendered the world to the screen. I switched the shader to a masking shader I made here, set the mask to the world, and rendered the lighting buffer.

Code is here.

I will attempt to break it down now.

I render the world to a frame buffer (it’s like a frame but stored in RAM). I do the same with the lighting shadows.

I render the background (the starry sky).
I render the world buffer to the screen. This is what’s actually drawn.
I render the lighting buffer to the screen using the shader mentioned above so that it becomes masked. Imagine the shadow as a piece of paper, and the world is the stencil in which you trace on the paper. What you traced is what’s left. Therefore, the traced shadow will only overlay on the blocks, and not the background.

Example: