0

I have read this answer, and i have tried it:

for (int i = 0; i < mapArray.GetLength(0); i++)
{
    for (int j = 0; j < mapArray.GetLength(1); j++)
    {
        if (mapArray[i, j] == null)
            CurrentLight = 0f;
        else
        {
            mapArray[i, j].Light = CurrentLight;
            CurrentLight += Tiles.Absorb;
        }
    }
}

and this is the result: Game Lighting

As you can see, my problem is that the lighting isn't realistic, the function goes through the columns and absorb the light by 0.05F if the current tile is a block, if it is not a block it will be reset to 0 (which is the full brightness).

The tree mainly get in my eye's and the lighting isn't that good.. how should i improve it?

thanks.

Community
  • 1
  • 1
Naor Hadar
  • 537
  • 1
  • 6
  • 19
  • 1
    "[lighting](http://en.wikipedia.org/wiki/Lighting)", not "[lightning](http://en.wikipedia.org/wiki/Lightning)". – Oliver Charlesworth May 10 '14 at 13:14
  • 1
    You should try setting different "absorb" values for blocks. For example, trees only decrease lighting by `.01f`. You should also look at [my question and this answer](https://gamedev.stackexchange.com/questions/40563/how-can-i-acheive-a-smooth-2d-lighting-effect) on _smooth_ lighting. See where you get, I can write a more thorough answer when I'm home. – Cyral May 10 '14 at 14:35
  • 1
    **ALSO:** Almost forgot, you should run your lighting pass in every direction, that will make it much smoother. Copy the loops 3 more times, but change the direction (Not just top to bottom, also loop left to right, right to left, and bottom to top) – Cyral May 10 '14 at 14:36
  • Adding 3 more loops causing a huge drop on frame rate. – Naor Hadar May 10 '14 at 14:51
  • @NaorHadar Well you can either cache the data, use the GPU or both. – Matan Shahar May 10 '14 at 14:53
  • @ISun how can I do that? – Naor Hadar May 10 '14 at 14:56
  • 1
    @NaorHadar caching means saving the light data into a map (a 2D array of floats) and instead of calculating the light each time you use this map, the you'll have to have some update mechanism that updates only the light values that should be updated. Also by looking at your code it seems that it's very inefficient, you calculating the light values for the entire map while you should update the ones that the camera is able to see. – Matan Shahar May 10 '14 at 15:03
  • @ISun I guess i could work with chunks and update the lighting only where there was a change in the blocks (update just the area around the change). – Naor Hadar May 10 '14 at 15:05

2 Answers2

1

The most important phenomenon in this scenario is light scattering. Light scattering takes place in every direction, not only downwards as you modelled it. You can calculate the lighting with a big linear system of equations, but this is potentially slow (see radiosity). Here is an approximative alternative:

The basic idea is that a block distributes its light to all neighbouring blocks. We start with a 1 for unfilled blocks and 0 for filled blocks (note that I use 0 for "no light" and 1 for "full light"; this seems to be more intuitive"). After we have initialized all blocks with their respective values, we can start the following distribution pass (I omitted range checks; that's up to you):

float[, ] incomingLight; // should have the same size as the map 
                         // and saves the additional light for each block
for each i,j in the map
    float myLight = (map[i, j] == null ? 1 : map[i, j].Light)       
    incomingLight[i - 1, j    ] += 0.10f * myLight //Add light to the left neighbour
    incomingLight[i - 1, j - 1] += 0.07f * myLight //top left neighbour
    incomingLight[i    , j - 1] += 0.10f * myLight //top neighbour
    //do the same for all remaining neighbours

    incomingLight[i, j] -= (sumOfWeights) * myLight //preserve overall light intensity
    // ...
next
for each i,j in the map
    if(map[i, j] != null)
        map[i, j].Light = min(1, map[i, j].Light + incomingLight[i, j])
    incomingLight[i, j] = 0
next

Now we have illuminated all blocks that are adjacent to a light block. You might need to adjust the weights for the distribution.

We can repeat this pass to create a more realistic lighting. The more often the pass is executed, the deeper the light will scatter. Note that each pass will brighten up the entire scene because unfilled blocks have basically an infinite light intensity.

Another approach, which should be faster but slightly more imprecise, is to calculate the distance of each block to the nearest unblocked tile and use this as a coefficient for the lighting. There are very efficient algorithms to calculate the distance (see distance transform). Calculating the block distance (Manhattan distance) is quite simple and should suffice in your case:

Initialize filled blocks with infinity or a very big number and unfilled blocks with zero. Then do four passes:

float [,] distances //initialized with 0 or infinity

//from top left corner
for y from 0 to height - 1
    for x from 0 to width - 1
        distances[x, y] = min(distances[x, y], distances[x - 1, y] + 1, distances[x, y - 1] + 1)

//from top right corner
for y from 0 to height - 1
    for x width - 1 to 0
        distances[x, y] = min(distances[x, y], distances[x + 1, y] + 1, distances[x, y - 1] + 1)

//from bottom left corner
for y from height - 1 to 0
    for x 0 to width - 1
        distances[x, y] = min(distances[x, y], distances[x - 1, y] + 1, distances[x, y + 1] + 1)

//from bottom rightcorner
for y from height - 1 to 0
    for x width - 1 to 0
        distances[x, y] = min(distances[x, y], distances[x + 1, y] + 1, distances[x, y + 1] + 1)

After those four passes you have the block distance to the nearest free block in the distances array.

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
1

Your scene shows a sectional image of the mountains. The objects on the surface cannot cast shadows to the section (i.e. into the inside of the mountain). Sunlight can only hit the surface, i.e. the grass, the trees and the avatar.

I would let the light come from top left and brighten up the top left part of the first objects it hits. Because of the 2D appearance of your scene it is difficult to get natural shadows, as those always imply a 3rd dimension.

enter image description here

You could even darken the bottom right parts of the objects.

enter image description here

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188