Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

Advanced search

1037936 Posts in 41930 Topics- by 33552 Members - Latest Member: MajorBulldozer

September 01, 2014, 05:36:40 AM
TIGSource ForumsDeveloperTutorials2D Shadow Effects
Pages: [1] 2
Author Topic: 2D Shadow Effects  (Read 53309 times)
Level 3

View Profile WWW
« on: October 25, 2009, 10:11:03 AM »

What's this about?
The aim is to produce shadow effects for 2D games in realtime.  A notable example of this in action would be Gish or Hitlers Must Die.  Sadly I have nothing to do with the making of those games though.

A demo of what we'll be making:

Click to go to an interactive version.

I do most stuff with Flash so I'll use Actionscript during this, but the principles are general and it could be applied to whatever you like to work with.  If you've never met Actionscript you'll be able to read it just fine.  I also make use of Box2D's b2vec2 class in some example code.  Again no need for you to be familiar with it; it's a pretty standard class to deal with 2D vectors.

The aim of this tutorial is to explain the concepts in use by means of prose, diagrams and code.  It'll not give you a finished shadow-drawing engine at the end but it should teach you how to make one yourself, and that's much more fun!

Casting shadows from polygons
Before we can draw the shadow coming from a shape, we need to have the shape represented somehow.  Polygons are a good way to do this - it means we represent the shape as a series of connected lines that form a solid 2D shape.  The demo above shows a crowd of regular polygons, but it'll work just as well for irregular and even concave polygons.  Plenty of games aren't limited to just geometric shapes, but as with collisions and physics it is often useful to simplify shapes to an approximate polygon.

How you store these polygons is something I'll leave up to you as it will depend a great deal on what libraries you're using or what else you have going on in the game.  For instance re-using the shadow-casting polygons as collision-detection polygons will often work out as a good solution.  In the demo I have used Box2D to handle the physics, so have also used its own representation of the polygons.

Whatever shape our polygon is and however you represent it in code, it'll be constructed of straight lines.  The good thing about straight lines is they're nice and simple.  So we'll do the shadow casting on individual lines, and build those up into a shadow for the whole polygon.

Shadowing a Line
Here's how we want the shadow from a single line to look, with the orange circle as the light source, and red line as the shadow casting line:

So our task becomes drawing the shape of the shadow, which is given by the points A, B, C, D.  Points A and D are already known as they're just the start and end of the line.  B and C are quite easily found:

Think of point B as being point A after being moved.  But what direction to move it?  Easy!  Apply the same movement as would be needed to get from L (where the light source is) to A.

To put it in more mathematical terms:
Take the vector L-to-A, and add it to the position vector of A.

To put it in code terms, using the b2vec2 class from Box2D:

private function projectPoint(point_:b2Vec2, light_:b2Vec2):b2Vec2
var lightToPoint:b2Vec2 = point_.Copy();
var projectedPoint:b2Vec2 = point_.Copy();
return projectedPoint;

Of course the very same process can be used to find point C from D.

Drawing out the resulting shadow is a simple enough process.  Using the .graphics methods of a shape object for instance:

var shadows:Shape = new Shape();
var projectedPoint:b2Vec2;
shadows.graphics.lineStyle(1, 0, 0);
shadows.graphics.beginFill(0, 1);

shadows.graphics.moveTo(startVertex.x, startVertex.y);

projectedPoint = projectPoint(startVertex, light);
shadows.graphics.lineTo(projectedPoint.x, projectedPoint.y);

projectedPoint = projectPoint(endVertex, light);
shadows.graphics.lineTo(projectedPoint.x, projectedPoint.y);

shadows.graphics.lineTo(endVertex.x, endVertex.y);


Just repeat this process for every line in every polygon, and you'll have the full shadow of the shapes drawn out.  Done!  Fantastic!

A first optimisation
Now that it works, we can start on the long road of making it faster.  If you look carefully at the shadows in the demo you can see that only the lines that are facing away from the light actually influence the shape of the final shadow.  This means that we're processing and drawing the shadows for many lines which we don't actually need to.

Here a shape (shown in red) has shadows cast thanks to a lightsource.  See how the shadow lines that I have coloured a lighter grey fall entirely within the larger shadow, and so will not affect the final shadow image?  We've spent precious time drawing out those shadows, but the player never actually sees the result - what a waste!

The good news is that it's simpler than you might think to determine if it's worth drawing the shadow from a line.

You might notice that it's the lines facing towards the lightsource that we don't need to draw shadows from.  Using a little vector maths we can quite easily find which lines are facing the lightsource, and so which are worth drawing shadows for.

You can consider each line as splitting the world into two parts: the world on the left of the line and the world on the right.

Take note that which side is left and which is right will depend on the direction you're considering the line from.  So make sure you always consider the lines of your polygons as going from one point to the next along in a clockwise direction (or counterclockwise if you like - just be consistent.)

In this case we're going clockwise, and we can see that for this particular line the lightsource falls on the left hand side.  Check the other lines, and you'll find that only lines where the lightsource falls to their right will cast a shadow that actually needs to be drawn.

We can use a little bit more vector mathematics to determine if the lightsource is to the left or right of any particular line:

First we find the normal of the vector that goes from the start to the end of the line, then take the dot product of that and the vector that goes from the lightsource to the start of the line.  The dot product will be a number, but we're only interested in if it's positive or negative, as that will indicate what side the lightsource is on.

I'll not try to fully explain the vector maths that just happened, but I'll give a taste.  The normal of a vector is a vector that is at right-angles.  It's very easy to find - just swap around the x and y components of the vector, and multiply one of them by -1.  Which one you multiply by -1 will determine if you get the normal that points to the left of the vector or to the right.  Taking the dot product of two vectors gives us a number (not a vector) which can be used for other clever things, but for now all we're worried about is that it is positive if the two vectors point in the same direction, and negative if they point away from one another (and zero if they're perpendicular.)

Effectively we are drawing a new line at right-angles out of the line we're considering, and asking if it points towards the light source.  If it does, we know not to bother drawing the shadow for this line.

In code:

private function doesEdgeCastShadow(start_:b2Vec2, end_:b2Vec2, light_:b2Vec2):Boolean
var startToEnd:b2Vec2 = end_.Copy();

        var normal:b2Vec2 = new b2Vec2(startToEnd.y, -1 * startToEnd.x);

var lightToStart:b2Vec2 = start_.Copy();

if (dotProduct(normal, lightToStart) < 0)
return true;
return false;

private function dotProduct(vecA_:b2Vec2, vecB_:b2Vec2):Number
return (vecA_.x * vecB_.x + vecA_.y * vecB_.y);

There's still plenty more to cover on the topic; off the top of my head:
Using BlendMode to make shadows be shadows instead of black voids (which is slightly more Flash-centric, although I'm sure you can get the same effect with OpenGL)
Different ways to determine the length of shadows.
Creating a "torch beam" effect.
Dealing with multiple and coloured light sources.
General graphical consistency considerations.

I hope someone will find this part useful at least.  Questions, comments?  What part was hardest to understand?

If you like, the source for the simple example is available here. There's a FlashDevelop project in there, but the important stuff is just two .as files that can be opened in any text editor. It uses Box2D for the shapes, so if you're not familiar with that then the source may be needlessly confusing.
« Last Edit: December 24, 2011, 08:57:26 AM by Salt » Logged
Level 3

View Profile WWW
« Reply #1 on: October 25, 2009, 10:34:16 AM »

Applying shadows using Blendmode
In Part One we drew out some plain black shadows, but we want to use these shadow shapes for something more than drawing black areas on the screen.  As everyone* knows, shadows aren't black but just darken whatever would normally be there.  Flash can do this nice and speedily by using BlendModes.

Very briefly, the BlendMode of a display object decides how it is drawn on top of other objects.  By default everything has BlendMode.NORMAL which just draws objects over one-another.  But there are a wide variety of BlendModes available which vary how the colours of overlapping objects will be mixed or combined to produce the finished image.

We are interested in using BlendMode.HARDLIGHT.  A display object set to HARDLIGHT will have its colours altered as if it were illuminated by whatever colours lie "beneath" it.  Stick a picture of a nebula over the demo from Part One, and set that picture's blendmode to HARDLIGHT, and you get:

Easy.  To be more exact on how this BlendMode works: Any colour value 128 (80 in hexadecimal) or over will cause the corresponding colour value in the image on top to be increased, colour values below that will cause a decrease.  It's easiest to understand thinking of a greyscale "shadow map" placed under your pretty image.  Dark areas on the shadow map will darken the image, light areas will lighten it.  It's an extreme effect, with any black areas on the shadow map resulting in a black area on the finished image and white areas producing pure white.  Generally your shadow map should vary between 0x303030 for dark but visible areas and 0x7f7f7f for fully lit areas.

Limited range lights
In Part One we dealt with just drawing shadows in what was assumed to be an infinitely large pool of light.  It is very simple to adapt this to drawing an illuminated area that contains shadows.

Each frame, instead of just clearing the area you draw the shadows to instead fill it with a dark "ambient light" colour.  Now draw on a circle (or cone, or whatever torchbeam-like shape you like) of light colour.  Then just draw on your shadows as normal in the dark ambient light colour.

You now only need to draw the shadows of objects which lie within your circle of light.  For instance box2D's World class provides a Query function which will allow you to find all the objects that fall within a specified area.

Extending shadows
Depending on the effect you wish to create, you may prefer your shadows to project out to the edge of your lit area.  Sadly I have been unable to find a neat and tidy way of doing this.

It is possible to project each point of the shadow you are drawing to the edge of the ring of light by changing your projectPoint function to:

private function projectPoint(point_:b2Vec2, light_:b2Vec2, lightRadius_:Number):b2Vec2
var lightToPoint:b2Vec2 = point_.Copy();

var extraLengthNeededToReachRadius:Number = lightRadius_ - lightToPoint.Length();

var vectorToAdd:b2Vec2 = lightToPoint.Copy();

var projectedPoint:b2Vec2 = point_.Copy();
return projectedPoint;

However as this projects the points that mark out the corners of the shadow shape that is then drawn using straight lines, there will be small segments of the light circle left unshadowed which should be in shadow.

My best solution so far is to fluff it and just add "a bit" on to the length of the projection vector so that the shadow shape is pushed far enough out.  I'm still working on other possibilities though.

Tile-based shadows
Sticking games in tiles is very popular, because it makes most things easier.  Good news is it makes shadows a little easier too!

Let's assume you have a tiled world something like this:

The red tiles are solid walls, which we'll assume block all light.  We can make it work by just considering each wall tile as a square which will cast a shadow just like any other polygon.  But there's plenty of opportunity for speeding things up!

Firstly we know that the edges of the wall tiles are always going to be straight along either the X or Y axis.  This means we can replace the fiddling about with vectors that was needed for polygons to determine if each edge should cast a shadow with some even simpler less-than and greater-than comparisons.

The biggest optimisation will come from being able to ignore some edges entirely.  This image shows all the wall tile edges present in our example:

That's a lot of edges.  By running the map through a simple trimming algorithm, a very large number of them can be ignored.  For each wall tile, check if the neighbour to its right also blocks light; if it does, then the wall tile's right edge can be ignored.  Repeat that for all the wall tiles and for all their edges and you'll end up with:

Depending on the size of your map this can take a while (it quickly becomes a huge number of checks to perform once your map is of a reasonable size.)  But the operation only needs to be performed when the map is first generated/designed.  Should your game involve the creation or destruction of walls, then you'll need to repeat the check on the neighbours of any tile that is changed.

You could further reduce the number of edges by joining adjacent edges to make a few long continuous straight edges.  I prefer not to do this, and instead keep things on a per-tile basis so that I can more easily limit what section of the map the game is having to deal with at any one time.

A further optimisation would be to perform LoS checks on tiles, so that those which are completely hidden from the lightsource by other tiles will not have their shadows drawn.  As I use quite large tile sizes, I found that this check would rarely eliminate more than a couple of tiles on the screen so I felt that it wasn't worth pursuing.  However if you use a smaller tile size, and tend to have certain types of level design then this check would become much more worth while.  The classic Rogue style levels of large open rooms connected by narrow passages would particularly benefit from LoS checking on tiles as you could often safely exclude whole rooms.

Visual consistency
This is a big topic.  Visual consistency deals with making sure your shadows make sense to the player.

In 3D graphics it's easy to make shadows make sense as they just need to appear as they do in real life or as close to that as you can manage with your technology.  The point is that there's an obvious end goal - to get as close to how the shadow really looks.

In 2D graphics making shadows look "right" depends a great deal on other aspects of the game's visual design.  The main problem is very few games are really showing a 2D world.  Instead they show a 2D view of a strangely flattened 3D world.  A good example to pull out at this moment are most the 2D Zelda games:

(image copyright Nintendo)

The walls look fine, until you think about them too much.  This room would need to have some deeply odd geometry for the view seen in the screenshot to be actually possible.  Try projecting realistic shadows over it and you'll just highlight how the graphical presentation shows a world that doesn't make sense.

Know what you're dealing with
It's important to have a clear understanding of what assumptions you're making about the world you're depicting in your game; otherwise you risk going against those assumptions when you add shadows and producing something that feels wrong to the player.

Let's take the example shadow caster from Part One and see what the shadows are telling the player about the world:

The shadow from each object has an end.  This means that the light source must be positioned higher (closer to the player's viewpoint) than the tops of the objects.

The shadows from all the objects are the same length when they're the same distance from the light.  This means all the objects are exactly the same height as one-another.

The shadows start at the base of the objects.  This means that the objects are sat on the surface that they're casting shadows on.

The shape of the shadows is determined only by the relative position of the light compared to the objects.  This means that the surface the shadows are being cast on is perfectly flat.

If this shadow caster was used in a game which broke any of these assumptions, the player will notice.  They may not realise what they're noticing but they'll almost always get a "this isn't quite right" feeling.  A huge slab of the human brain is dedicated to visual processing, and extracting information from things like shadows is something we're very good at - even if we're not aware of it.

Shadows on shadow casters
This is a problem I've wrestled with a great deal.  To know what I'm talking about, let's consider the walls I talked about in the tile-based section:

Wall A has a shadow being cast upon it, so shouldn't it be dark?  But how much of it should be dark?  Consider also how the wall which is casting the shadow should be shaded.  The edges facing the light source should be lit up, but the shadow casting sides should be in shadow.  Often the tops of walls are drawn on in these kinds of top-down views, but how should that be illuminated?  If we're being realistic about it, no light from below the wall's level would ever reach it and so it would be in total darkness.

These are difficult problems that need to be carefully considered, and your solutions to them are likely to have wide-reaching implications within your game.

Thankfully there is a cop-out that generally looks okay: Simply have all shadow casters be unaffected by shadows.  It's surprising how little this impacts the perceived quality of the finished image.  But do take care to have the colours of the shadow-casters be fairly muted, else they'll stand out too much from the rest of the shaded world.  Similarly avoid the use of very deep shadow else they will look out of place.

Here's a graphical Rogue-like prototype making use of the cop-out:

In this case the walls should have been a darker colour so that they do not stand out so clearly against the shadowed floor areas.

Avoiding the cop-out
Cop-outs are useful, but let's be awesome instead.

Choosing the right graphic style can make incorporating shadows much easier.  For this example I am using a pixel-art "2.5 topdown" view.  This means that it's kind of a top-down view, but one of the vertical sides of objects are visible to the player.

Here's shadow casting in the same prototype without the cop-out:

Not bad really, but there's a couple of problem areas.  Area A shows where a shadow overlaps the vertical section of a wall.  Area A doesn't look too shocking, but it is wrong - a shadow will not form the same shape on a vertical section of wall as it would on the horizontal floor.  Area B is more of a problem; here a wall casts a shadow but the wall on the shadowed side is still brightly illuminated.

The good news is we can fix both areas with the same technique.  We will simply project the shadow as it appears at the base of the wall up the full height of the wall.  Just loop along the length of the wall, reading the value of the shadow map at the base (using BitmapData's getPixel function) and fill in the row of pixels directly above it.  It's faster to use fillRect to do that rather than multiple setPixel calls.  Hey, I actually mentioned some Flash stuff!

As if by magic:

It isn't perfect of course.  It should be possible to position the player so that her shadow ends half way up the wall, but that will never happen using this system.  To get that effect you would need to work with the relative heights of the light source and shadow casting object, as well as volume information on the form of the shadow caster.  But that's just making a 3D game, which is cheating.

If you have vertical parts of the wall visible on all four sides of a tile (or just two sides, as in an isometric view) then you can use just the same technique for projecting shadows up those walls.  I made a prototype for a "all four sides visible" top-down view but wasn't quite satisfied with the results.  The technique should work quite nicely with an isometric view.

Not shadowing the character
Having shadows cast by an irregularly shaped object such as the player character not overlap with the character sprite itself is a fiddly business.

This problem doesn't occur in the case of walls or polygons, as the shadows only start at the far edge of the shape.  However the character is casting a shadow as if she were a simple polygon, when she is actually a quite different animated image.

My solution is that when the shadows from the character are drawn, the sprite is then drawn over the shadow using BlendMode.ERASE.  This effectively takes a character-shaped bite out of the shadow being cast by her.

It is possible for this to backfire, with this hole in the shadow being visible even if the character is actually hidden behind another object.  Sadly I haven't been able to find a more satisfactory solution.

Matching your graphics
You may notice that in the screenshots above the shadows all have rough edges rather than the nice anti-aliased lines of the demo from Part One.  Similarly there is a uniform pool of light, rather than a more realistic gradient fading out as it moves from the light source.  These are both efforts to keep the shadows fitting in with the visual style of the rest of the game.  Just because you're doing clever programming doesn't mean you can ignore the fact that gradients and pixel art don't mix, or that aliased and anti-aliased lines don't mix.

Similarly, you will need to keep in mind the impact that covering much of your graphics with light and shadow effects is going to have on their appearance.  Similar colours will look all the more similar when seen in shade, which may cause you to lose details - make sure those details aren't important to the player.  If you light a dungeon with moody orange light from candles, then everything will take on that orangey tone.  These can be great at building atmosphere, but you need to keep the effect of lighting in mind when you're making your assets.

Multiple lights
What's better than one pretty light effect?  TWO pretty light effects!

This is two identical candles sat next to one-another.  We want areas where light from both reach to be brighter than areas which get light from only one.  To achieve this, we first draw out the light/shadow effect for each candle separately:

And add them together!

Oh dear.  The problem here is that the sum of the light from two candles is actually over 100% illumination.  Being able to create over 100% illumination is useful for special effects (nuclear blasts, flashbang grenades, or walking from indoors to out in a 'next gen' game.)

With a bit of clever use of layering and blendmodes, we can cap the brightness of the light map:

var lightMapLimiter:Bitmap = new Bitmap(new BitmapData(vWidth, vHeight, false, 0x7f7f7f));
var shadowLayer:Sprite = new Sprite();
lightMap.blendMode = BlendMode.DARKEN;


shadow.blendMode = BlendMode.HARDLIGHT;
Here lightMap is the end product of drawing all the scene's lights using blendMode.ADD, gameLayer is where the walls, player and everything else in the game world is drawn.  lightmapLimiter is just a big block of colour the size of the screen - whatever colour it is will be the cap for lightMap.

And something to play with
Finally you can wander around that rogue-like prototype if you like, though there's nothing to do but walk around looking at shadows.  Press space to make your faerie companion light up.

Click here

I feel like this post was a bit wandering, and long.  Hopefully there's useful parts in it for some people.

Further reading
Some discussion and further explanation of this tutorial.
A fine person made a shadow caster with a little help from this tutorial using HTML5's Canvas thing!
« Last Edit: December 15, 2011, 07:55:34 AM by Salt » Logged
Level 10

View Profile
« Reply #2 on: October 30, 2009, 11:02:46 AM »

I had to work all this out myself IN C++ from a crappy orangy tangy article on gamedev.net (looking back at your post you probably used it)

anyway, I'm sure loads of people will find this useful, looking back over it after my rage it seems to be the exact same algorithm I used. It might be helpful if you have advice on how to extend the shadows, I did it for my game and I prefer the effect of an infinite shadow.

(if you want a live demo and explanation for the algorithm and its effects, heres somthing I made eairler: http://www.youtube.com/watch?v=D2eF8g46AmY)

(btw I also used box2d, it really makes the shadows/polygons easy doesn't it?)

in fact, i prefer unpleasant forums
Level 0

View Profile Email
« Reply #3 on: November 12, 2009, 04:08:22 AM »

This lack of posts in this thread has been bugging me, so I thought I might as well jump in. First off, brilliant tutorial, I have been looking to implement something like this for a while, and this finally tipped me over the edge. I am in the process of putting this into a tile based system, so I am looking forward to the next part, if you decide to do it.  Beg

Really, I just wanted to let you know that I thought this was awesome, and was in desperate need of bumping  Sad
Level 10

View Profile
« Reply #4 on: November 12, 2009, 10:34:08 AM »

It is awesome isint it?

if you want to use it with tile based things, just treat every tile as a separate polygon. I'd recommend doing a line of sight thing for each of the tiles so that you're not testing EVERY tile in the whole map.

On another note although I really like this technique and way of rendering shadows, so much I even use it in my game. However I have found that it doesn't scale very well with multiple lights. I've found that with multiple lights you can get shadows in lit places.
I'm considering using a similar technique to actually render the LIGHT instead of the shadow, but I haven't got around to it yet, nor will I probably.

in fact, i prefer unpleasant forums
Level 3

View Profile WWW
« Reply #5 on: November 24, 2009, 02:44:16 PM »

Very glad to hear you like it!

Please find a meandering part two added above.
Level 10

View Profile
« Reply #6 on: November 25, 2009, 04:26:35 AM »


in fact, i prefer unpleasant forums
Hayden Scott-Baron
Level 10

also known as 'Dock'

View Profile WWW Email
« Reply #7 on: November 25, 2009, 05:11:43 AM »

Very interesting stuff! I love 2D shadow effects, but they are oh-so-tricky to get right. Smiley I'm glad your topic covers the problems.

Level 5

View Profile Email
« Reply #8 on: January 16, 2010, 12:06:26 PM »

Oh, I tried something like this months ago, but I'm a shit programmer. I made a the light a shape that moulds dynamically to it's surroundings it looks awful for many many different reasons, your attempt is much better, gj.
Level 10

View Profile
« Reply #9 on: January 16, 2010, 01:08:14 PM »

That would be really nice for a kinda cartoony game with light, or something like gish, with a tar or liquid enemy...

in fact, i prefer unpleasant forums
« Reply #10 on: January 16, 2010, 03:12:43 PM »

I would also like to express my appreciation for this tutorial. One of my little flash projects that I'll eventually be working on is a little game where the gameplay is based on light/shadow. It's so nice to have resources and help out there from helpful individuals who take the time to put together a piece of instruction for free for everyone. Thank you very much.
Level 4

View Profile
« Reply #11 on: January 25, 2010, 06:52:45 AM »

Awesome tutorial!
Level 0

View Profile
« Reply #12 on: April 14, 2010, 10:43:11 PM »

Nice tutorial!

I'm working on a prototype for a shadowing system myself lately, and your tutorial will come in use to optimize it a bit.
Level 1

View Profile
« Reply #13 on: April 17, 2010, 12:56:35 PM »

I gave shadow rendering a stab the other day and found it hard as hell and eventually gave up. Thanks for taking the time to post a tutorial on it Smiley it helps greatly.

View Profile WWW
« Reply #14 on: April 20, 2010, 05:48:25 AM »

It's quite strange to see many of my thoughts written out - I followed a fairly similar (very close, actually) process of thinking when designing my shadow engine. It's somewhat awesome to see that I wasn't completely fumbling in the dark with guesswork. You can see a demo of  one iteration at http://sidelinegame.blogspot.com/2009/10/behind-veil.html

It needs some updating, mostly in the form of stopping shadow overlaps, but I'm trying to work out a way that wouldn't stop the objects being able to shadow each other and still be decent looking and fast.

It's quite cool that you've tutorialized your stuff, too. I've been planning on chucking my code into open source once I've fixed it up a bit.

Pages: [1] 2
Jump to:  

Theme orange-lt created by panic