Saturday, September 29, 2012

Working With Shadows

The logical course of action after finishing lighting is to move on to shadows. I opted for a relatively simple, software-based approach. It involves casting rays from the light source to individual edges of the tiles in the collision map, and drawing black quads based on the rays.


In my current implementation, the shadows are independent of the other lights. Thus, shadows cast by one light will be left untouched if there is another light in the way. This obviously causes problems, and a better method that will calculate cumulative shadows is currently in progress. But for a single light, the following algorithm suffices. Hopefully some OpenGL beginners trying to create shadows can gain some knowledge from my own trial-and-error!


The steps are as follows:
  • Determine the light position.
  • Cast a ray from the light to the first vertex (top-left point)
  • Calculate slope and extend the ray, so instead of it being from the light to the vertex, it goes from the vertex to some really distant value.
  • Keep this ray for later (I'll call it TopLeft later).

  • Repeat this process for each vertex, so you get 4 rays, which I'll call TopLeft, TopRight, BottomRight, and BottomLeft in the example code below.






  • Now that we have determined the sectors for shading, we test to see which quads are necessary to fill. This is not shown in the diagrams, but in practice, if the light is above the tile, the sector created by the TopLeft and TopRight rays will cut across the tile itself, which is obviously undesirable. The following must be true:
    • TopLeft.Start.y < LightPos.y     Draw quad
    • TopRight.Start.x > LightPos.x    : Draw quad
    • BottomRight.Start.y > LightPos.y : Draw quad
    • BottomLeft.Start.x < LightPos.x  : Draw quad


  • Draw the quads with whatever rendering method you wish, be it using glBegin()/glEnd(), glDrawArrays(), FBOs, or what have you.
Here is some C++ example source code using immediate mode rendering. CRay2 is just a class containing two CVector2's, which basically are an (x, y) coordinate pair. There is a minor bug with this method, when the LightPos.x is equal to the Ray.Start.x value, the shadow is not created as intended. When I find a fix, I'll update this code. If anyone has a fix, feel free to comment below.



And finally, here's a screenshot of Collapse using shadows with a single light in-game.

No comments:

Post a Comment