So I've implemented deferred rendering in GLSL and OpenGL for my game engine. I want to blend together multiple lights, but the performance is a bit sub-par. How I'm doing it now is the following:
- Render all the objects in the scene to a G-buffer storing the position, color and normals
- Take that data and render a fullscreen quad for the sun.
- Enable additive blending with glEnable(GL_BLEND) and glBlendFunc(GL_SRC_ALPHA, GL_ONE)
- For ever additional light in the scene render a fullscreen quad and send the shader all the data for each light.
The lighting looks great, but the performance is bad. I have been under the impression that deferred rendering allows for very little performance loss when rendering additional lights, and it is possible to render a few thousand lights with relatively little performance hit. Im sure the way Im doing this isn't efficient at all, so I need a bit of input. The first thing that comes to mind is making it so I don't have to blend so many quads and just make a loop in the shader and only render 1 extra quad. I store all my light data in structs, and the only way I've seen to pass structs to GLSL is by setting each individual variable using glUniform
.
Here is my current code:
void World::renderWorldAndLight()
{
//first thing we need to do is render the sun's shadow.
if (Lighting.Sun.needsShadowUpdate == true)
{
Lighting.setupDepthImage();
//render all the objects.
for (int i = 0; i < objects.size(); i++)
{
Lighting.renderLightPos(&Lighting.Sun);
objects[i].renderObjectForDepth();
}
Lighting.Sun.needsShadowUpdate = false;
}
//render the rest of the light shadows
//reder the Gbuffer
glBindFramebuffer(GL_FRAMEBUFFER, Lighting.gbuffer.frameBuffer);
GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glDrawBuffers(4, buffers);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0, 0, 0, 0);
for (int i = 0; i < objects.size(); i++)
objects[i].renderObjectWithProgram(Lighting.gbufferShader);
//now we have the gbuffer, lets render the sun
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glLoadIdentity();
//render the buffers for debugging
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, ENGINE_WIDTH, ENGINE_HEIGHT, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_CULL_FACE);
//glDisable(GL_BLEND);
//render the full screen quad for the sun
glUseProgram(Lighting.sunShader);
glUniform1i(glGetUniformLocation(Lighting.sunShader,"normalMap"),0);
glUniform1i(glGetUniformLocation(Lighting.sunShader,"albedoMap"),1);
glUniform1i(glGetUniformLocation(Lighting.sunShader,"positionMap"),2);
glUniform1i(glGetUniformLocation(Lighting.sunShader,"shadowMap"),3);
glUniform3fv(glGetUniformLocation(Lighting.sunShader,"sunPos"), 1, &glm::vec3(-Lighting.Sun.position.x,-Lighting.Sun.position.y,-Lighting.Sun.position.z)[0]);
glUniformMatrix4fv(glGetUniformLocation(Lighting.sunShader, "shadowMatrix"), 1, GL_FALSE, &Lighting.Sun.lightMatrix[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Lighting.gbuffer.textures[1]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, Lighting.gbuffer.textures[2]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, Lighting.gbuffer.textures[0]);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, Lighting.sunlight.depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_QUADS);
glTexCoord2f(0, 1);
glVertex2f(0, 0);
glTexCoord2f(1, 1);
glVertex2f(ENGINE_WIDTH, 0);
glTexCoord2f(1, 0);
glVertex2f(ENGINE_WIDTH, ENGINE_HEIGHT);
glTexCoord2f(0, 0);
glVertex2f(0, ENGINE_HEIGHT);
glEnd();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glUseProgram(Lighting.lightShader);
glUniform1i(glGetUniformLocation(Lighting.lightShader,"normalMap"),0);
glUniform1i(glGetUniformLocation(Lighting.lightShader,"albedoMap"),1);
glUniform1i(glGetUniformLocation(Lighting.lightShader,"positionMap"),2);
glDisable(GL_DEPTH_TEST);
for (int i = 0; i < Lighting.Lights.size(); i++)
{
glUniform3fv(glGetUniformLocation(Lighting.lightShader,"light"), 1, &Lighting.Lights[i].position[0]);
glUniform3fv(glGetUniformLocation(Lighting.lightShader,"lightcolor"), 1, &Lighting.Lights[i].color[0]);
glBegin(GL_QUADS);
glTexCoord2f(0, 1);
glVertex2f(0, 0);
glTexCoord2f(1, 1);
glVertex2f(ENGINE_WIDTH, 0);
glTexCoord2f(1, 0);
glVertex2f(ENGINE_WIDTH, ENGINE_HEIGHT);
glTexCoord2f(0, 0);
glVertex2f(0, ENGINE_HEIGHT);
glEnd();
}
glEnable(GL_DEPTH_TEST);
}
If there is any information, tips, tricks advice, it would be greatly appreciated. Thanks.
VBO
's instead of the immediate mode (glVertex*
). Don't render fullscreen quads for each light - determine what area will each light take up on the screen and useglViewport
. Also, as always, inspect your code and make sure you make as little state changes as possible. \$\endgroup\$