3
\$\begingroup\$

I'm trying to implement a simple "lighting" system that alters a value of light between 0.0-1.0, 1.0 is midday 0.0 is total blackness.

Are there any "good" information regarding this particular subject or is it something that you have to develop yourself and adjust to your game in particular. What I'm probably looking for is an algorithm that is flexible and could work by changing values like midday point and such.

My algorithm uses 12:00 / 12PM as it's center point and at that time it's 1.0 in lighting. What I'm trying to do is perhaps "change" the midday point to any value to say 10 and it should still work the same, and perhaps also be able to alter the number of hours the midday can be. Perhaps so that the lighting rises and falls exponentially. So that between night and 4 it raises slowly, between 4 and 8 it raises much faster and at 10 it's reached it high point until 14 when it starts to fall again but in the reverse order.

My simple algorithm only calculates a value between 0.0 and 1.0 and if there is any good info or some suggestions on how to "improve" this method or any other information regarding this subject it would be greatly appreciated.

My algoritm goes as following:

If (CurrentHour < (HoursPerDay / 2))

    Lighting = CurrentHour / (HoursPerDay / 2)
    Lighting += (CurrentMinute / MinutesPerHour) * (1 / (HoursPerDay / 2))

Else

   Lighting = 1 - ((CurrentHour / (HoursPerDay / 2)) - 1)
   Lighting -= (CurrentMinute / MinutesPerHour) * (1 / (HoursPerDay/2))

MIN_VALUE = 0.3f;
Max(MIN_VALUE, Min(Lighting, 1f))

or in C# code:

public float CalculateLighting(uint hour, uint minute, uint minutesPerHour, uint hoursPerDay)
{
    float lighting = 1f;

    if (hour < (hoursPerDay / 2))
    {
        lighting = (hour / (hoursPerDay / 2)) * lightingMax;
        lighting += ((minute / minutesPerHour) * (1f / (hoursPerDay / 2))) * lightingMax;
    }
    else
    {
        lighting = lightingMax - (((hour / (hoursPerDay / 2)) * lightingMax) - lightingMax);
        lighting -= ((minute / minutesPerHour) * (1f / (hoursPerDay / 2))) * lightingMax;
    }

    lighting = MathHelper.Clamp(lighting, lightingMin, lightingMax);
}

This way when the time is 6:00 AM the value is (6/12) and the same for (12+6) and also I clamp the results so I get a value between 0.3 and 1.0 or change that to whatever in case you wanted it to be Winter season so it's darker you can raise that value. This is however a very simple implementation of this concept.

Some functions that I'd like to bring in to it is:¨

  1. When is the "midday" and how long is it? Say it's 4 hours between 10-14. During this time it should always be 1.0 in lighting.
  2. Change the midday point to anytime or remove it completely. During dark nights in a game perhaps the sun shouldn't rise fully.
  3. "Somehow" perhaps make it increase slow and decrease fast by using some value for each. If the sun rises slowly but falls rather quickly afterwards. Exponentially raise and exponentially fall faster.

Just some ideas I'm looking for. It doesn't have to be realistic, just a sense that the weather can change. This value should be a value between 0.0 and 1.0 or adjustable (for extra bright days or something) due to the fact that it can be used to light the entire scene. A reference point to how bright it is in the game world.

How can I best extend my algoritm and improve it and perhaps implement new features?

Any suggestions are appreciated or some information on good implementation of a Day-Cycle system.

\$\endgroup\$
0

3 Answers 3

5
\$\begingroup\$

My guess would be that daylight is somehow related to a sine wave rather than a line or exponential curve. For example,

public float CalculateLightning(float hour, float minute, 
                                uint minutesPerHour, uint hoursPerDay)
{
    float time = (hour + (minute / minutesPerHour)) / hoursPerDay;
    float light = lightningMax * Math.Sin(2*Math.PI * time);
    return MathHelper.Clamp(light, lightningMin, lightningMax);
}

Changing whether the light is centered on noon — you're looking for “solar noon”. To change this you can add a constant to time.

Changing how much the sun rises — a bit more complicated but there are some wikipedia articles about it. Or if you want to be super precise, look at the demo and source of this page.

\$\endgroup\$
5
  • \$\begingroup\$ Also what I was thinking: that there's a sin/cos way to do this, and that light change at sunrise/sunset is not linear as it's based on light going through progressively more and more atmosphere. \$\endgroup\$
    – Tim Holt
    Apr 7, 2013 at 19:40
  • \$\begingroup\$ I tried using this method in a for loop and the results are kind of weird, CalculateLightning(i, 0, 60, 24); and the results are (lightningMax = 1, lightningMin = 0.3) = 0-1: 0.3, 2 : 0.5, 3 : 0.7, 4: 0.8, 5: 0.9, 6: 1, and then from 6 AM it goes down again until 11 o'clock where it stays at 0,3 for the remainder of the "day". I guess it's a start and it needs some optimization. It clearly needs to be something similiar, a wave, a curve that increases, stays the same and decreases. I don't think that the sun should be dark at 11. :) \$\endgroup\$
    – Deukalion
    Apr 8, 2013 at 2:15
  • \$\begingroup\$ Yes, it's “centered” at the wrong point (at 6am); you have to add a constant to time to recenter it where you want. Probably -½π; I'm not sure. \$\endgroup\$
    – amitp
    Apr 9, 2013 at 2:15
  • \$\begingroup\$ Changing Math.PI to Math.PI * 0.5d works, but if I were to center at some other time? Also, the question remains on how to retain the same value during some of the values. Preferable without any ifs statements, just an algorithm that works that into it. Or perhaps add a constant with lightningMin, lightningMax, so it multiplies it somehow with the max value. Say I want a value between 0,0 and 2,0 then add that to the constant. Like in my method. \$\endgroup\$
    – Deukalion
    Apr 9, 2013 at 8:52
  • \$\begingroup\$ Perhaps calculate so that (HoursPerDay / 2) - MiddayHours (2) is a reference point to when the value should be max and (HoursPerDay/2) + MiddayHours(2) it should start to decrease, so always set to 1*lightningMax between those hours or otherwise decrease the value. \$\endgroup\$
    – Deukalion
    Apr 9, 2013 at 9:14
1
\$\begingroup\$

in a previous app i had to implement when the sun is going down, based on the gps location. Take a look and maybe it will surve your purpose.

https://github.com/jeancaffou/Analemma (its in java though).

What it does is it can calculate the time of the sunrise/sunset for a given GPS coordinate (only latitude is needed - you can hardcode if you want to simulate a specific place on earth).

This way you can get when the sunrise and sunset is going to take place and from that you can calculate your light value

curTime = currentTime;
sunset = sunsetForDate(curTime.year,curTime.month,curTime.day);
sunrise = sunriseForDate(curTime.year,curTime.month,curTime.day);

if(currentTime >= sunrise && currentTime <= sunset)
{
     // daylight
} else 
{
     // night
}

The way you calculate the value is totally up to your needs. You can say that the light value is 1 at the mid point between sunset and sunrise eg. (sunset-sunrise)/2. And the light value is 0 when the current time is bigger than sunset + SOME_CONSTANT and smaller than sunrise - SOME_CONSTANT.

\$\endgroup\$
1
  • \$\begingroup\$ This seems good if you wish to make a simulation, but since I'm only looking for a simple system that is flexible enough perhaps not the best implementation. I just need to calculate a value that can used to set the light to the entire scene, and the priority is to get a single day working and then later perhaps add extra features. \$\endgroup\$
    – Deukalion
    Apr 8, 2013 at 3:09
1
\$\begingroup\$

I would suggest to use the logistic function to simulate the sunrise and sunset.

float t <- time of day from 0.00 to 23.99
float rise <- time of sunrise
float set <- time of sunset
float f <- factor to vary length of sunset and sunrise
float max <- max. intensity of light during the day
float min <- min. intensity of light during the day

if(t > 0 && t < 12)
  lighting = P((t - rise) * f)
else
  lighting = 1.0 - P((t - set) * f)

lighting = min + lighting * (max - min)

You can achieve a yellowish/reddish light at sunrise and sunset by calculating each of the RGB channels of your light separately. The sunrise for the red channel then should start earlier resp. later for the sunset than for the other channels.

\$\endgroup\$
4
  • \$\begingroup\$ Looking at the curve it seems promising, is it "rising" from 0-12 and falling from 12-24? But my only question is: how do I know what P is? If I want to implement it and look at the results. \$\endgroup\$
    – Deukalion
    Apr 10, 2013 at 2:17
  • \$\begingroup\$ Also, looking at the "Double logistic function" is perhaps something to look at also because it rises, stays and rises again. Perhaps if it were possible to rise, stay and fall with that curve. \$\endgroup\$
    – Deukalion
    Apr 10, 2013 at 2:19
  • \$\begingroup\$ according to the wiki article: P(x) = 1 / ( 1 + e^-x). The fact that it seems to reach 0 at -6 and 1 at 6 is just coincidence, it is only approaching but never reaching 0 and 1 \$\endgroup\$
    – Dirk
    Apr 10, 2013 at 8:02
  • \$\begingroup\$ For the double logistic function I don't think it is possible... \$\endgroup\$
    – Dirk
    Apr 10, 2013 at 8:03

You must log in to answer this question.

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