I am toying around with "modern" OpenGL (this is my first attempt, I am new to this) and I have been developping my own deferred shading renderer as an exercise.

I am using an FBO with the following attachments:

  • DS : Depth/Stencil (GL_DEPTH32F_STENCIL8)
  • RT0: Position (GL_RGB16F)
  • RT1: Normal (GL_RGB16F)
  • RT2: Albedo (GL_RGB16F)
  • RT3: Postprocessing buffer 0
  • RT4: Postprocessing buffer 1

Depth + RT0, 1, 2 are filled first in one single geometry pass.

Then for each light source (point lights only for now, spheres are used as light volumes):

  • Stencil pass to limit light calculation to where it actually has an effect
  • Light pass: light contribution is accumulated in RT3

From this point on, I am only rendering a quad the size of the screen and fragment shaders are used to compute the next image.

Albedo (RT2) and Light (RT3) are blended into RT4: I now have my lit image.

I can enable more postprocessng shaders (FXAA, Depth of field, ...), in which case RT3 and RT4 are used for ping-ponging.

The thing is, even with no postprocessing, not that many lights (around 10) and a very simple scene (roughly 5k vertices), my frame rate drops like significantly as soon as I increase resolution or add more lights.

It looks like my light pass is responsible for most of this but how can I track down what exactly is slowing everything down? Are there any tricks to optimize it? Is there anything I am missing?


Light Fragment Shader for reference:

uniform sampler2D position; 
uniform sampler2D normal; 
uniform float screenw; 
uniform float screenh; 

uniform vec3 light_pos; 
uniform vec3 camera_pos; 
uniform vec4 diffuse; 
uniform vec4 ambient; 
uniform vec4 specular; 
uniform float outer_radius; 
uniform float inner_radius; 

float specular_hardness = 100.0; 

vec4 calc_point_light(vec3 p, vec3 n) 
    vec3 light_dir = light_pos - p; 
    float d_to_light = length(light_dir); 

    light_dir = normalize(light_dir); 

    vec3 eye_dir = normalize(camera_pos - p); 
    vec3 half_vec = normalize(light_dir + eye_dir); 

    float attenuation = (outer_radius - d_to_light) / (outer_radius - inner_radius); 
    attenuation = max(min(attenuation, 1.0), 0.0); 

    vec3 c = ambient.rgb * ambient.a;
    c += diffuse.rgb * max(dot(n, light_dir), 0.0) * diffuse.a; 
    c += specular.rgb * pow(max(dot(n, half_vec), 0), specular_hardness) * 

    return vec4(clamp(c, 0.0, 1.0) * attenuation, 1.0); 

void main(void) 
    vec2 texcoord = gl_FragCoord.xy / vec2(screenw, screenh); 
    vec3 p = texture2D(position, texcoord).xyz; 
    vec3 n = texture2D(normal, texcoord).xyz; 

    gl_FragColor = calc_point_light(p, n); 

1 Answer 1


Some things you can try:

  1. Don't use a separate position buffer. Instead reconstruct position from the depth buffer (bound as a texture).

  2. Use GL_RGB10_A2 for your normal buffer. This is two-third the size of GL_RGB16F and should provide enough accuracy.

  3. GL_RGBA8 should provide enough accuracy for your albedo buffer.

  4. It should be fairly quick to try different depth buffer formats (GL_DEPTH24_STENCIL8 or GL_DEPTH_COMPONENT24) to see if it makes any difference.

Following these recommendations should significantly reduce the texture traffic during your light pass, which might improve performance as bandwidth is often the limiting factor here.

  • \$\begingroup\$ This is incorrect! For a framebuffer to be complete and valid in OpenGL, all color attachments must use the same internal format. \$\endgroup\$
    – vallentin
    Jul 22, 2016 at 3:11
  • \$\begingroup\$ @Vallentin Really? Can you support that with a reference? I can't see anything in the spec to prohibit using different formats. \$\endgroup\$
    – GuyRT
    Jul 22, 2016 at 16:58
  • \$\begingroup\$ I stand corrected. What I said only applies when using GL_EXT_framebuffer_object, and not when using either core or ARB version, no matter if it's OpenGL 3.3 or 4.4. If you're curious about the GL_EXT_framebuffer_object restriction, then you can find it at " Framebuffer Completeness". \$\endgroup\$
    – vallentin
    Jul 23, 2016 at 13:32

Not the answer you're looking for? Browse other questions tagged .