Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm implementing point lights in my Voxel engine, and I'm really struggling to get a good flow of light, from 100% near the light source to 0% at the light radius.

I have 5 arguments for the function:

  1. Light color (Vec3)
  2. Light intensity (distance from the light till the distance where falloff is 100%)
  3. Distance from the light to the fragment
  4. The angle from the fragment normal to the light
  5. The position of the light

Can anyone push me in the right direction to create a function for the calculating of the fragment color?

Image of one of my experiments:

Voxel Engine Per-Fragment Lighting Test

Edit (current code requested by Byte) Note that this is just some experiment code from my side. I got the float att from a website, and it kinds works, but far from perfect.:

void main()
{
// Light color
vec3 torchColor = vec3(1.0f, 1.0f, 1.0f);

float lightAdd = 0.0f;
for (int i=0; i<5; i++) {
    vec3 pos = lights[i];
    if (pos.x == 0.0f) continue;

    float dist = distance(vertex_pos, pos);
    if (dist < 9) {
        float att=1.0/(1.0+0.1*dist+0.01*dist*dist);
        vec3 surf2light = normalize(pos - vertex_pos);
        vec3 norm = normalize(normal);
        float dcont=max(0.0,dot(norm,surf2light));
        lightAdd += att*(dcont+0.4);
    }
}

vec3 textureColor = texture2D(texture, texture_coordinate).rgb;
vec3 torch_output = lightAdd * torchColor;

vec3 final_color = ((0.1+torch_output) * textureColor);

gl_FragColor = vec4(final_color, 1.0f); 
}
share|improve this question
2  
Without more info, we don't know if we're pushing you in a direction you've already tried. The lighting looks good, and for all we know (except that you've said otherwise) it could be what you're looking for. Tell us what about it isn't working and what you've tried already. –  Byte56 Jun 6 '13 at 0:44
2  
Can you edit the question to include your current code for calculating the light values? –  Byte56 Jun 6 '13 at 1:00
1  
When you're editing, I suggest you try to get away from subjective terms like "Good-Looking" as well. Be specific in what your requirements are, "good looking" is not specific enough :) –  Byte56 Jun 6 '13 at 1:11
6  
You're still saying things like "struggling to get a good looking, natural lights" and " works, but far from perfect". You need to include specific, exact language. We don't know what good looking is to you, or what natural lights look like to you, or what perfect is. –  Byte56 Jun 6 '13 at 1:56
2  
Have you tried removing if (dist < 9)? Alternatively you could calculate att with a function that returns 1 when distance is 0 and 0 when distance is 9. E.g. mix(1.0, 0.0, dist / 9.0) –  msell Jun 6 '13 at 5:32
show 2 more comments

1 Answer

up vote 6 down vote accepted

The attenuation function you've got,

att = 1.0 / (1.0 + 0.1*dist + 0.01*dist*dist)

is a fairly common one in computer graphics - or, more generally, 1.0 / (1.0 + a*dist + b*dist*dist)) for some tweakable parameters a and b. To understand how this curve works it's helpful to play with the parameters interactively. This curve is nice because it approaches the physically-correct inverse square law at large distances, but it doesn't shoot up to infinity at short distances. In fact, with a = 0 it's a pretty good model of a spherical area light.

However, one disadvantage of it is that the light never quite goes to zero at any finite distance. For practical purposes in realtime CG we generally need to cut lights off at a finite distance, as you're doing with the if (dist < 9) clause. However, the radius of 9 is too short - with your settings in the attenuation function, the light doesn't get close to zero until dist is around 100.

You can calculate the radius of the light from the b parameter in the attenuation function (since the quadratic term dominates at large distances). Let's say you want to cut the light off when the attenuation reaches some value minLight, like 0.01. Then set

radius = sqrt(1.0 / (b * minLight))

That gives a radius of 100 for b = 0.01 and minLight = 0.01. Alternatively, you can set the radius and calculate b to match:

b = 1.0 / (radius*radius * minLight)

For radius = 9 and minLight = 0.01, that gives b = 1.23. You can set it up either way, but the key is to make the radius and the attenuation function match so that you don't cut the light off until the attenuation function is already very low, so you won't see a sharp edge.


All that said, there are alternative attenuation functions you can use. Another fairly common one is:

att = clamp(1.0 - dist/radius, 0.0, 1.0); att *= att

or the slightly fancier:

att = clamp(1.0 - dist*dist/(radius*radius), 0.0, 1.0); att *= att

Play with the parameters for those ones too. These curves have the advantage of going exactly to zero at the given radius, while still looking kind of like the natural inverse square law.

share|improve this answer
    
Perfect answer. Thanks alot. –  Basaa Jun 6 '13 at 18:46
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.