FrameBuffer / Multi light

Started by
24 comments, last by WhiskyAndCode 5 years, 4 months ago

I've not worked in a deferred engine in 6-8 years now and OpenGL in about as long.  Here is an open source engine that uses OpenGL and C/C++ that seems fairly modern: https://github.com/barrypevans/RenderingEngine

You'll have to dig through his source code as it's not in a tutorial form.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Advertisement
On 11/26/2018 at 7:50 PM, WhiskyAndCode said:

the author leaves fixed the amount of light (32 lights)

This is just a tutorial, meaning that the purpose is only to show how to do some particular technics.

1 hour ago, WhiskyAndCode said:

Yeah, I've been looking for this for at least 6 months.

Why not sending an uniform to GL that will represent the number of active lights in your current scene ?

Just now, _Silence_ said:

 

Why not sending an uniform to GL that will represent the number of active lights in your current scene ?

Hi _Silence_, thank u for reply.

Because my project will have countless sources of lights, the weapons will be laser-type and the players will be able to shoot fire (there's fire light too), put up campfires. That is, I will focus a lot on lighting.

 

..You use the G buffers to store normals and all that stuff needed for lighting, then for each light you render either a mesh that represents it (scaled unit sphere for point lights etc), or you can calculate 2d screen coords and render simple quads or something else... But no matter how you do it, the lighting shader reads from the G buffers and outputs using additive blend. Then you can render as many lights as you want to, and I suppose you should output to a 16 bit/channel output buffer and do some HDR/tonemapping to bring it down to 0..1 intensity. You can do a fullscreen quad pass to add ambient light, since that shouldn't be added for each light, but only once.

 

.:vinterberg:.

3 hours ago, WhiskyAndCode said:

Because my project will have countless sources of lights, the weapons will be laser-type and the players will be able to shoot fire (there's fire light too), put up campfires. That is, I will focus a lot on lighting.

Actually @_Silence_ was giving you a very helpful piece of advice and what you will actually have to do.  Which is send how many lights you have in that scene to the shader for rendering.  Your program (game) will have to know how many lights you are going to render and so will your shader.  They used a fixed amount as he said for the demo, as it is merely that, a demo.  Sounds like you might be better off using an existing engine like Unreal or Unity.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Thank you all.

11 minutes ago, WhiskyAndCode said:

Thank you all.

 

What vinterberg told is what should be done when doing deferred rendering with many lights. You'll however have to optimize a lot in order to avoid to compute where a light does not cast and adapt your shader.

This is, essentially, what you should be doing! There is no limit to the number of lights you can draw in deferred rendering, but there is a cost to draw them (very cheap shader), the final lighting computation works per pixel on the result of ALL lighting, not per light, so costs for drawing lights are relatively low! We can have hundreds, or thousands of lights, per frame. The next bottleneck is materials.

On 11/26/2018 at 10:50 AM, WhiskyAndCode said:

Hey guys, thank u for all reply.

English is not my native language and I find it difficult to express myself.
But come on, following the learnopengl tutorial:
https://learnopengl.com/Advanced-Lighting/Deferred-Shading
the author leaves fixed the amount of light (32 lights) as shown by the GLSL:



#version 330 core
out vec4 FragColor;
  
in vec2 TexCoords;

uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;

struct Light {
    vec3 Position;
    color;
};
const int NR_LIGHTS = 32;
uniform Light lights [NR_LIGHTS];
uniform vec3 viewPos;

void main ()
{
    // retrieve data from G-buffer
    vec3 FragPos = texture (gPosition, TexCoords) .rgb;
    vec3 Normal = texture (gNormal, TexCoords) .rgb;
    vec3 Albedo = texture (gAlbedoSpec, TexCoords) .rgb;
    float Specular = texture (gAlbedoSpec, TexCoords) .a;
    
    // then calculate lighting as usual
    vec3 lighting = Albedo * 0.1; // hard-coded ambient component
    vec3 viewDir = normalize (viewPos - FragPos);
    for (int i = 0; i <NR_LIGHTS; ++ i)
    {
        // diffuse
        vec3 lightDir = normalize (lights [i] .Position - FragPos);
        vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Albedo * lights [i] .Color;
        lighting + = diffuse;
    }
    
    FragColor = vec4 (lighting, 1.0);
}


And when it comes to applying the lights:



glBindFramebuffer (GL_FRAMEBUFFER, 0);

        // 2. lighting pass: calculate lighting by iterating over screen filled quad pixel-by-pixel using the gbuffer's content.
       
        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        shaderLightingPass.use ();
        glActiveTexture (GL_TEXTURE0);
        glBindTexture (GL_TEXTURE_2D, gPosition);
        glActiveTexture (GL_TEXTURE1);
        glBindTexture (GL_TEXTURE_2D, gNormal);
        glActiveTexture (GL_TEXTURE2);
        glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);
        // send light relevant uniforms
        for (unsigned int i = 0; i <lightPositions.size (); i ++)
        {
            shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Position", lightPositions [i]);
            shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Color", lightColors [i]);
            // update attenuation parameters and calculate radius
            const float constant = 1.0; // note that we do not send this to the shader, we assume it is always 1.0 (in our case)
            const float linear = 0.7;
            const float quadratic = 1.8;
            shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Linear", linear);
            shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Quadratic", quadratic);
        }
        shaderLightingPass.setVec3 ("viewPos", camera.Position);
        // finally render quad
        renderQuad ();

 

The tutorial can be tweaked to greatly increase the number of lights.
Set NR_LIGHTS to some high value like 512, this is now your "maximum number" of lights on screen.

Then pass in a uniform for the number of active lights, the shader will exit the for loop. There's no need to render geometry per light, that makes it more complicated. This does a single pass that loops over all of your lights per pixel. 

This topic is closed to new replies.

Advertisement