Jump to content
  • Advertisement
WhiskyAndCode

OpenGL FrameBuffer / Multi light

Recommended Posts

Hello everyone.
I'm following lessons from learnopengl.com and concludes the chapter on "Deferred Shading". I confess that I am a lighting enthusiast in games. And unfortunately I did not find anything to explain with using as many lights as I want at runtime, I just found examples showing with limited light source:

for (int i = 0; i <NR_LIGHTS; ++ i)
     {
         vec3 lightDir = normalize (lights [i] .Position - FragPos);
         vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Albedo * lights [i] .Color;
         lighting + = diffuse;
     }

Looking at google I found some things about accumulating information in framebuffer, however I did not find a code or anything else explained.
Could someone explain to me how I could do this?
A pseudocode with the openGL commands would be fine.

Right now I thank you all.

Edited by WhiskyAndCode

Share this post


Link to post
Share on other sites
Advertisement

What research into deferred rendering have you done?  Another OpenGL tutorial on the subject that took all of 10 seconds to find: http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html

This is older but still usable: http://download.nvidia.com/developer/presentations/2004/6800_Leagues/6800_Leagues_Deferred_Shading.pdf

Then there is this: https://www.trentreed.net/blog/deferred-rendering-pipeline/

Just a great site overall, this is more of an advanced topic in deferred rendering but his other articles are great too: https://mynameismjp.wordpress.com/2016/03/25/bindless-texturing-for-deferred-rendering-and-decals/

Hope these help answer your questions.

Share this post


Link to post
Share on other sites

Hello CrazyCdn, thanks for the reply.
Up to this point I understood and implemented. However, what I want to know is how to accumulate lights in the framebuffer so I can use the amount of light source I want in a dynamic way.
I found this tutorial (http://www.redway3d.com/downloads/public/documentation/wf_deferred_shading.html), and a very interesting pseudocode:

for each object
{
   render object to the g-buffer
}
for each light
{
   render light geometry using the g-buffer to perform lighting
}

which is almost the same in nvidia pdf that you informed me:

For Each Object:
     Render lighting properties to "G-buffer"
For Each Light:
     framebuffer + = brdf (G-buffer, light)

But I do not know how to "render light geometry using the g-buffer to perform lighting" / framebuffer + = brdf (G-buffer, light) (accumulate lights in framebuffer)

when I add the lights in a loop, only the last one is active (blue light):

glm::vec3 lightColor[] = {glm::vec3(1,0,0), glm::vec3(0,1,0), glm::vec3(0,0,1)}; //Red, Green and blue

glm::vec3 lightPos[] = {glm::vec3(0,3,0), glm::vec3(-2.09, 3, 7), glm::vec3(-2,3,-3)};

for(int i = 0; i < 3; i++){
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    glEnable(GL_DEPTH_TEST);
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   shaderCubeLight.use(); //glUseProgram(programID);

   //..............//

   //summarizing the draw cube (already setted MVP).......

   shaderCubeLight.setVec3("lightPosition",  lightPos[i]);

   shaderCubeLight.setVec3("lightColor", lightColor[i]);

   glBindVertexArray(cubeVAO);
   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, cubeTexture);
   glDrawArrays(GL_TRIANGLES, 0, 36);

   glBindFramebuffer(GL_FRAMEBUFFER, 0);
   glDisable(GL_DEPTH_TEST);
   // clear all relevant buffers
   glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT);

}

 

Share this post


Link to post
Share on other sites

The idea for 'drawing lights' using deferred rendering is rather different to other lighting techniques - for each light, you need to actually draw a mesh, whose shape and size correspond to the 3D volume of the light - for a point light its a sphere, and for a spotlight, its a cone - directional lights are handled separately. I would recommend this very old tutorial :see #35, 36 and 37 - its not a perfect solution, but it will help you to understand how the lighting phase works in deferred rendering.. I once used it as a basis for a more complete deferred renderer, which was used my bachelor degree final project, the logic is fairly sound.. effectively, for each pixel which represents a solid point on a surface, we accumulate in one of our gbuffer textures, the influence of all lights in the scene whose volume contains the point in worldspace representing that pixel - we don't do the full lighting calculation, we just gather the intensity (and colour) of all lights that fall on a pixel, we DEFER the final lighting calculation... and once we've processed ALL lights, we do a final pass, and using all the knowledge of all our gbuffer textures, we do the final lighting calculation per pixel for the output texture.

BDRF (bidirectional reflection distribution function) is used for a specific surface material effect, and can be added later, as it is material-specific, your garden-variety 'Lambert' shader can be written as a first step.

 

http://ogldev.atspace.co.uk

Edited by leith
more specific information to clarify the answer, and corrected a typo

Share this post


Link to post
Share on other sites

You accumulate lights by using an additive blend function, so every light you render adds to the output buffer :)

Share this post


Link to post
Share on other sites
25 minutes ago, vinterberg said:

You accumulate lights by using an additive blend function, so every light you render adds to the output buffer :)

Yep, but you also need to clamp its intensity, and deal with some way to mix the colours of the lights, whichever suits you - we have some options here, it's not as simple as adding numbers and clamping to 1

 

 

Share this post


Link to post
Share on other sites

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 ();

I wish I could do this:

uniform Light lights;
uniform vec3 viewPos;

void main ()
{
    // retrieve data from gbuffer
    vec3 FragPos = texture (gPosition, TexCoords) .rgb;
    vec3 Normal = texture (gNormal, TexCoords) .rgb;
    vec3 Diffuse = texture (gAlbedoSpec, TexCoords) .rgb;
    float Specular = texture (gAlbedoSpec, TexCoords) .a;
    
    // then calculate lighting as usual
    vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
    vec3 viewDir = normalize (viewPos - FragPos);
   
        // diffuse
        vec3 lightDir = normalize (lights.Position - FragPos);
        vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Diffuse * lights.Color;
        // specular
        vec3 halfwayDir = normalize (lightDir + viewDir);
        float spec = pow (max (dot (Normal, halfwayDir), 0.0), 16.0);
        vec3 specular = lights.Color * spec * Specular;
        // attenuation
        float distance = length (lights.Position - FragPos);
        float attenuation = 1.0 / (1.0 + lights.Linear * distance + lights.Quadratic * distance * distance);
        diffuse * = attenuation;
        specular * = attenuation;
        lighting + = diffuse + specular;
   
    FragColor = vec4 (lighting, 1.0);
}

And in C ++:
 

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.Position", lightPositions[i]);
            shaderLightingPass.setVec3("lights.Color", lightColors[i]);
            // update attenuation parameters and calculate radius
            const float constant = 1.0; // note that we don't 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.Linear", linear);
            shaderLightingPass.setFloat("lights.Quadratic", quadratic);
            shaderLightingPass.setVec3("viewPos", camera.Position);

          //My doubt is here


        }
        renderQuad();

 

My doubt is here, as I apply the lights above in the framebuffer, as if it were:
configures light, persists in framebuffer, configures light, persists in framebuffer, configures light, persists in framebuffer, configures light, persists in framebuffer ....

I'm sorry if I'm bothering too much, but this is very complicated to understand only in theory.

 

 

Share this post


Link to post
Share on other sites

That's my question too, comrade.
Nowhere do I find this information, just this simple theory and nothing else.

😫

Share this post


Link to post
Share on other sites
3 hours ago, Highwayman said:

That's my question too, comrade.
Nowhere do I find this information, just this simple theory and nothing else.

😫

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!