# 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 on other sites

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

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/

##### 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.

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

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

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

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 on other sites

can anybody help me ?

Edited by WhiskyAndCode

##### 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.

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

##### Share on other sites

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

##### 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 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:
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);
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;
shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Linear", linear);
}
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);
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++)
{
// 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;

//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 on other sites

Nowhere do I find this information, just this simple theory and nothing else.

😫

##### Share on other sites
3 hours ago, Highwayman said:

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.

## Create an account

Register a new account

• 9
• 15
• 9
• 9
• 56