2D light diffuse map artefact

584 views Asked by At

I am trying to implement a simple 2D light engine on my game based on libGDX. The idea is to create this light engine without shaders. I am not planning to render any shadow for the moment. I have managed to render a light map with triangles and gradients, but the problem is that when multiple light source overlaps I got some artefact like in this picture:

enter image description here

the yellow light source is producting a dark border when overlapping the other light sources.

I am using the following blend function to render the lights:

Gdx.gl.glBlendFunc(GL20.GL_ONE, GL20.GL_ONE);

Is there a way to get rid of this dark border?

The gradient goes from yellow to black full opacity. As GL_ONE is suppose to be additive, the colors should only get brighter right?

Update 1:

Using shader as explained by Nico Schertler is definitely better, but there are still some parameters I am not sure.

enter image description here

here is the fragment shader I came up with

void main() {
  float intensity = 0.02;
  vec2 pos = vPosition;
  float dist = distance(pos, u_origin) / u_radius;
  vec4 color = vec4(intensity * (vColor.rgb / ((dist * dist) + 0.01)), 1);
  gl_FragColor = color;
}

the problem is I that am not sure about the constants intensity = 0.02 and 0.01 I have added to the function to make it look nice. Also, the radius variable is now the max between screen height and width.

Update 2:

I have finally opted for the following equation:

void main() {
  float dist = distance(vPosition, u_origin) / u_radius;
  vec4 color = vec4((vColor.rgb) / (pow(dist, 2) + 0.01), vColor.a);

  gl_FragColor = color;
}

Then combined with an occlusion map (to darken non illuminated places) based on the same equation. Here is the final output:

ambient + occlusion

1

There are 1 answers

3
Nico Schertler On BEST ANSWER

That depends very much on how you splat the lights.

E.g. if you use some truncated quadratic function as the light's brightness (with respect to the distance from the center), i.e. something like that:

enter image description here

If you add two lights, you'll get this profile:

enter image description here

enter image description here

In this profile, you see a clear discontinuity. The reason for this discontinuity is the truncation of the original function. So you most likely have any form of truncated light function in your image (be it quadratic, linear or whatever), which causes the bright area in the intersection of the two lights.

So how to solve this? Physically correct light distribution falls off with the quadratic distance (in 2D you could also argue for a linear falloff), x is the distance from the light source:

light = factor / x^2

There are two problems with this function. Firstly, it is not compactly supported. That means that you would have to draw an infinitely big splat (or at least as large as the screen). This might not be a big problem. You could also cut off the light as soon as it becomes very small. The second problem is more severe. If x goes to 0 (i.e. you are directly on the light position), you cannot calculate this value (it tends towards infinity because all light energy is focused on a point with no area). So it really depends on what you want to do with the light map. Of course, you could regularize in a way similar to

light = factor / ((x)^2 + epsilon)

, where epsilon is a very small number. If your scene is something that is viewed from above, this epsilon could be the height of the light over the ground (squared). Then, you would get a profile like the following:

enter image description here

Voila, no discontinuities. But you will most likely need a fragment shader to achieve this.

If you don't care about physical correctness, a Gaussian splat might also give nice-looking results:

light = factor * exp(-x^2 / variance)