Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to handle tons of 2D lights using GLSL shaders efficiently?  (Read 8093 times)

0 Members and 1 Guest are viewing this topic.

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Hello. I have a problem I've been trying to fix for about 2 weeks but couldn't find anything that would help me.
Everything started when I stumbled upon a 2D light shader that works flawlessly and does everything I want it to (which is basically creating these cool point lights):

#version 450

uniform vec2 lightPos;
uniform vec3 lightColor;
uniform float intensity;

out vec4 color;

void main()
{
        vec3 lAtt = vec3(0.01, 0.1, 0.00001);
       
        float dist = distance(gl_FragCoord.xy, lightPos.xy);
        float att = 1.0 / (lAtt.x + lAtt.y * dist + lAtt.z * dist * dist);
        vec4 finLight = vec4(vec3(att) * lightColor, 1.0) * intensity;
       
        color = finLight;
}

How I drew these lights was pretty simple: I rendered every single object in the scene and used this shader on a render texture, which I found out to be very efficient because I'm applying the light only once for each frame and not for every object for each frame.

However, that was not something I wanted in the long run due to the fact that it only worked for ONE light but since I'm in development of a game that obviously requires more than one light (particles, lights around objects, etc.) and so I started to look for a better solution than this.

The first thing I came up with is a buffed version of this shader which allowed me to render up to 256 lights and which worked pretty well (and was WAY more efficient with rendering a hundred lights in comparison to the next version of it which I'll explain in a moment):

#version 450

struct Light
{
        vec2 position;
        vec3 color;
        float intensity;
};

uniform int lightCount;
uniform Light lights[256];
uniform sampler2D texture;
uniform vec2 size;

out vec4 clr;

void main()
{
        vec3 lAtt = vec3(0.0001, 0.0001, 0.001);

        vec2 uv = gl_FragCoord.xy;

        vec4 outc = vec4(0.0);

        for (int i = 0; i < lightCount; i++)
        {
                float dist = distance(lights[i].position, uv);
                float att = 1.0 / (lAtt.x + lAtt.y * dist + lAtt.z * dist * dist);
                outc += vec4(vec3(att), 1.0) * lights[i].intensity * vec4(lights[i].color, 1.0);
        }

        vec4 pixel = texture2D(texture, uv / size);

        clr = pixel * outc;
}

So everything was fun and all, but this solution seems to be hated all over the internet since I'm passing HUGE amounts of data to the shader (which I heard is not recommended) and it still had a light limit which I did not like because it's obvious that for many particles or huge maps this is a real pain in the .... .

So I kept looking for solutions, and found one that, yes, allowed me to draw as many lights as I wanted, but on the other hand used a TON of resources of my GPU (GTX 1070).

How it worked was simple: I used the first version of the lighting shader, but used this shader multiple times in this manner:

pseudocode:

    for each object:
        draw every object to a rendertexture
    for each light:
        draw each light to another render texture with the lighting shader and add up the results with additive blending

    multiply the lights' and the scene's texture to achieve a lit scene

    draw the lit scene

 

So, now, here's my question: what did I do wrong? How can I improve this so it can render even 10,000 lights?

Any help is appreciated!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #1 on: March 29, 2018, 10:28:03 am »
So what exactly is wrong with your final solution? It looks ok: one RT for the scene, another for the lights, and then you multiply to get the final result. I don't see anything wrong with it.
Laurent Gomila - SFML developer

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #2 on: March 29, 2018, 10:34:14 am »
Yes, but when I draw a 100 lights, this is what I get: (screenshot attached)
It gets even worse with 500 lights, where the framerate drops to 40.
« Last Edit: March 29, 2018, 10:37:23 am by Fililip »

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #3 on: March 29, 2018, 10:42:18 am »
BTW, below's the real code responsible for this.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #4 on: March 29, 2018, 10:44:08 am »
If the bottleneck (*) is the shader, then you can draw the lights as simple sprites with no shader: find/build a black & white image of a light, then you just have to change its position, size and color before drawing.

Also, no need to call display() after every draw on the RT; this might kill your performances too.

(*) you might want to try OpenGL profilers instead of guessing ;)
« Last Edit: March 29, 2018, 10:45:47 am by Laurent »
Laurent Gomila - SFML developer

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #5 on: March 29, 2018, 10:55:15 am »
Sorry, I'm not really "prepared" yet to go deep into OpenGL (I'm a huuuuge newbie), I would really love to be able to make 2D games with just SFML.
As for the shader, I don't think it is the bottleneck, but rather drawing 500x the lights (which are essentially fullscreen quads, so it's like drawing to 500 screens at the same time) (am I right though?)
But I'd really like to make this effect with shaders because I know it can be done efficiently and it would be a good place for me to start learning shaders.
So is there any way for you to help me with this?
« Last Edit: March 29, 2018, 11:01:02 am by Fililip »

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #6 on: March 29, 2018, 11:32:24 am »
BTW, will culling work for this? (rendering only the light-affected surface)
If yes, how can I achieve it?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #7 on: March 29, 2018, 12:43:14 pm »
I suggested 2 simple potential improvements, don't you want to test them?

Quote
Sorry, I'm not really "prepared" yet to go deep into OpenGL (I'm a huuuuge newbie), I would really love to be able to make 2D games with just SFML.
The OpenGL profiler would just be a way to identify the bottleneck, in order to know what to optimize. No need to go deep into OpenGL stuff.
Laurent Gomila - SFML developer

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #8 on: March 29, 2018, 02:53:35 pm »
I figured it out. It was the debug mode's slow performance. Compiling it in release mode improved the performance A LOT.
Now I can safely and efficiently handle even 400/500 lights without a problem.
My only question is: how did this guy () achieve such great results (10,000 lights and more than 10 fps)?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #9 on: March 29, 2018, 03:09:08 pm »
Quote
I figured it out. It was the debug mode's slow performance. Compiling it in release mode improved the performance A LOT.
Indeed, measuring performances in debug mode is pretty useless... :P

Quote
Now I can safely and efficiently handle even 400/500 lights without a problem.
That's great but I'm still not convinced that this is the most efficient way to do it.

Quote
My only question is: how did this guy achieve such great results (10,000 lights and more than 10 fps)?
Deferred rendering is a totally different way of doing lighting calculation: everything is done in screen space rather than world space. So basically the number of lights doesn't have much impact.

You can have a look at this recent SFML project for interesting explanations and links on the subject:
https://en.sfml-dev.org/forums/index.php?topic=23751.0
Laurent Gomila - SFML developer

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #10 on: March 29, 2018, 03:12:10 pm »
So how would I go about testing the performance of my application with the profiler?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #11 on: March 29, 2018, 03:32:58 pm »
I've never used it so you'll have to document yourself if you're interested :)
Laurent Gomila - SFML developer

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #12 on: March 29, 2018, 03:39:08 pm »
And also, how can I turn my lighting system into a deferred rendering one?
I get the main idea, but how are you passing lights to a deferred rendering shader?
The LearnOpenGL website has a fixed value of 32 lights in their example, and to me it isn't a better solution.
I would like to be able to render as many lights as I could think of without a big impact on performance.
So what's with the passes? How do they happen?

Fililip

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #13 on: April 01, 2018, 11:37:21 am »
Okay, I figured it out. I had to render a circleshape for each light and use the lighting shader on it, rather than on the entire fullscreen quad for pointlights. Now I can safely render even 2000 lights in the entire window's viewport, at which I'm somewhat-starting to get 80 or lower FPS, but that's still good imo. (it may be related to my GPU being pretty good at this though)
But can I go even further than that about it? What sort of other optimization technique can I use to make this app handle even more lights (if that's possible that is) with even less of a performance impact?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to handle tons of 2D lights using GLSL shaders efficiently?
« Reply #14 on: April 01, 2018, 05:54:15 pm »
Why do you still insist on using a shader to draw lights, since it basically always generates the same shape? Using a texture (and no shader) should be much faster.
Laurent Gomila - SFML developer