Saturday, March 12, 2016

xna - Grid-Based 2D Lighting Problems


I am aware this question has been asked before, but unfortunately I am new to the language, so the complicated explanations I've found do not help me in the least.


I need a lighting engine for my game, and I've tried some procedural lighting systems. This method works the best:


if (light[xx - 1, yy] > light[xx, yy]) light[xx, yy] = light[xx - 1, yy] - lightPass;
if (light[xx, yy - 1] > light[xx, yy]) light[xx, yy] = light[xx, yy - 1] - lightPass;
if (light[xx + 1, yy] > light[xx, yy]) light[xx, yy] = light[xx + 1, yy] - lightPass;
if (light[xx, yy + 1] > light[xx, yy]) light[xx, yy] = light[xx, yy + 1] - lightPass;

(Subtracts adjacent values by 'lightPass' variable if they are more bright) (It's in a for() loop)


This is all fine and dandy except for:



The system favors whatever comes first in the for() loop


This is what the above code looks like applied to my game: Light


On the left side you can see some weirdness going on: Problem


The loop goes top to down then to the right column: Problem


This is how the for() loop goes: for loop


Light provided from my mouse, should be a full light, but is only half: enter image description here


If I could get some help on creating a new procedural or otherwise lighting system I would really appreciate it!



Answer



Since your question is kind of broad, I'm going to give a kind of broad answer.


A recursive algorithm is a typical way to approach this. The algorithm "flows" outward until there's no more light to spread. Remember to put in a base case for your algorithm, or you'll never stop lighting. For example, if the next tile being selected is as bright or brighter than the current tile. Since you're likely to have large grids, you'll probably want to implement a list for your recursion instead of using the stack (it'll probably overflow). An example of this might be:



(note this is using the stack based approach, only recommended for shallow recursion, there's an example that's similar to the list approach below)


Light(x, y, value)
(x,y).lightvalue = value
if(value-1 > 0)
if((x-1, y).lightvalue < value-1)
Light(x-1,y,value-1)
if((x+1, y).lightvalue < value-1)
Light(x+1,y,value-1)
if((x, y-1).lightvalue < value-1)
Light(x,y-1,value-1)

if((x, y+1).lightvalue < value-1)
Light(x,y+1,value-1)

Clearly your case would be a lot more complex, since you want to account for sky light shining down, you'll have to keep track of if the tile has sky directly above it.


Another option is some kind of cellular automata. So each tile knows its state and can tell neighboring tiles they should update their state. Something like:


foreach(tile in tilesToUpdate)
if(tileToLeft.lightValue > thisLightValue + 1)
this.lightValue = tileToLeft.lightValue - 1
else if(tileToLeft.lightValue < thisLightValue)
tilesToUpdate.add(tileToLeft)

//repeat for other directions

Again, this would need to be modified to allow for your sky light requirement. You'd kick off this algorithm by setting the light value of one tiles and adding it to the list of tiles to update.


You might also find it's more optimal to update all the lighting using your existing method, while making a list of tiles that will likely need to be updated by one of the above methods. These are just very simple examples that would need to be significantly optimized for your use cases, but they're a start.


You can see a related question here where I went into a bit more detail about some aspects of lighting. And another related question that might be useful.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...