5
\$\begingroup\$

I'm working on a tile-based 2D pseudo top-down/side-on game very much in the graphical style of Prison Archiect. More specifically, I'm referencing their style of drawing wall tiles where you can see the south, east and west sides of the wall at once. Here's an example from my engine:

enter image description here

I'm also working on a pixel-based (i.e. not tile-based) lighting and shadows implementation. I'm currently struggling with trying to decide how/where to project from a light source on intersections with these wall tiles.

enter image description here

I can't decide where on these tiles should occlude light cast out by light sources. If the red highlighted areas are the "top" of the wall, and the blue highlighted areas are the "side" of the wall, I believe I have two options:

A) Only occlude light from the "top" of the wall

enter image description here

It's worth mentioning that I also plan to use UV-mapping so that only the walls facing the light source will be illuminated, rather than the pre-shaded tiles I'm using as an example. However, that would mean that the tiles adjacent to a wall in shadow may be lit and I don't think this would look quite right. Alternatively...

B) Occlude light from the entirety of the wall tile

enter image description here

This seems more realistic for the ground tiles but does not let me easily illuminate the wall "sides".

I'm not really happy with either solution so my question is: is there another alternative which will give more realistic shadow-casting in a 2.5D view? I'd also rather keep the sides of the walls visible rather than use a top-down only perspective as I feel this would force the rest of the art into a top-down perspective, rather than pseudo side-on.

\$\endgroup\$
0

3 Answers 3

11
\$\begingroup\$

Going to try and doodle up what I mean here as soon as I finish typing this, but:

What about merging the two?

Use the second (occlude by base) for everything that isn't a wall and the first (occlude by tops) for lighting the walls?

You actually did this by accident in your second example, with the wall that goes off the bottom of the image. Extending this to the remaining walls won't be perfect, but it would allow some lighting of the walls that will look pretty decent.

(Sigh, this computer doesn't have an layer-capable image editor, GIMP is taking forever to download.  Image coming less quickly than anticipated.)

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Thanks, that should work great if I can get the directional facing of the walls to pick up lightly correctly. I was slowly coming to this realisation but I think I was put off by the fact it'll probably double the computation required. Still, I think it's the answer I was looking for! \$\endgroup\$ Jan 18, 2016 at 20:43
  • 1
    \$\begingroup\$ @RossTurner Sure thing :) I'm sure there will still be some "odd" results (such as light leaking through corners to illuminate walls) but for what you're trying to do, I think the result will be sufficiently simple and sufficiently accurate. \$\endgroup\$ Jan 18, 2016 at 20:46
  • \$\begingroup\$ Nice - simple and effective, probably doesn't need too much additional work, and seems to fit the graphical theme a bit better than my answer. \$\endgroup\$ Jan 18, 2016 at 22:52
  • \$\begingroup\$ good idea. I would also add a falloff circle of additive ambiant light in the dark zones, to fake GI and add some mood. this version is very dark and limbo-like. depends on what stress/relief you want to achieve though. \$\endgroup\$
    – v.oddou
    Jan 19, 2016 at 2:10
  • \$\begingroup\$ @v.oddou The ambient light is much darker in this example than I'm actually planning for the finished result. Thanks for the input! \$\endgroup\$ Jan 19, 2016 at 9:11
1
\$\begingroup\$

I won't be able to make an image for you, but one trick you could do to figure out if a piece of wall should light up is to take advantage of the 'alpha' channel for determining the direction the pixel is facing, as opposed to the opacity of the pixel.

You could then determine whether the pixel should be lit between the light source and the facing of the wall pixel (alpha value). Flat Shading in 3d rendering is a cheap and effective method that usually uses a similar algorithm using the normal of the plane and the light source's position/color. In your case, the normal is interpreted from the alpha, but you could use a similar algorithm, resulting in very 3D lighting for a 2D game (which is probably overkill but still cool, in my opinion)

//some psuedocode of the important parts
Vector2 lightPosition;
Vector2 pixelPosition;

Vector2 lightDirection = lightPosition - pixelPosition;
Vector2 normalDirection = //(translate into a rotation Vector)

lightDirection.normalize();
normalDirection.normalize();
angleBetweenLightAndNormal = dotProduct(normalDirection, lightDirection);
//determine from the angle whether the normal should be lit or not

You could scale a value of 0.00 to be facing down (or whatever direction you prefer) and the value of 1.00 to also be down, as if the direction had rotated 360 degrees. This means the value of up is 0.50

If you plan on including the top of walls or the ground you may even scale it a few values short, and keep specific reserved values to mean top or ground.

In fact, if you needed it to be as simple as possible:

//0.00 = down
//0.10 = left
//0.20 = right
//0.30 = up

//0.40 = floor
//0.50 = top of wall

which then leaves plenty more values for other situations.


The drawback is that you are taking over the alpha channel, which generally relates to opacity. This means both that your engine may take some modifying to ignore alpha, and, if you do need an alpha channel, you can't do this method directly.

You could create a new image with only lighting information instead. The nice thing about creating a new image with lighting information is that your RGB values can fully translate into a 3-directional rotation for the normal that pixel is facing, and the alpha could be the "height" of the pixel (as if it were in 3D space)

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, that's a great suggestion. I've also been considering doing uv mapping with a uv map texture in the rgb channel but as I don't really need a Z component as the lighting is on the same level as the objects being lit, so storing a 360 degree normal in the alpha is a nice idea. Having said that it may get a bit tricky to draw, I guess is have to write a custom image viewer to see the information, though that shouldn't be too bad either. One to think about! \$\endgroup\$ Jan 19, 2016 at 9:05
  • 1
    \$\begingroup\$ This answer reminds me that La Mulana 2 is actually a 3D game precisely for lighting purposes. It still plays like a 2D side scroller, but the actual level geometry is pushed out directly towards the screen so that when they use standard point lights, statues and such cast shadows. This answer is using pixel information to approximate that kind of effect, the mental visualization I made reading the answer reminded me of that team's approach. \$\endgroup\$ Jan 19, 2016 at 15:30
0
\$\begingroup\$

I don't want to take anything away from @Draco18s' answer, but I went with his suggestion (combining the two) and ended up putting together a demonstration video on how it's done (for those interested) at https://www.youtube.com/watch?v=Cabl0LMmlgY

In addition to the quick sketch that he added, I ended up using normal maps on each "face" of the wall so that if there was any "bleed over" light, the angle of incidence means that it isn't illuminated.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .