title: Deferred Rendering - Is it valid to reconstruct position for a point light using a light volume?
body: <h2>Position reconstruction</h2>
  
  <p>I want to verify that this is a valid method and I'm not overlooking something.</p>
  
  <p>I am using a spherical mesh which I am using to only render the portion of the screen that the light overlaps. I rendering only the back-faces if the depth is greater or equal the depth buffer <a href="https://stackoverflow.com/a/14419722/2507444">as suggested here</a>.</p>
  
  <p>To reconstruct the camera space position of a fragment I am taking the vector from the camera space fragment on the light volume, normalizing it, and scaling it by the <strong>linear depth</strong> from my gbuffer (which is stored as a <strong>32 bit float</strong>). This is sort of a hybrid of the methods discussed <a href="http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/" rel="nofollow noreferrer">here (using linear depth)</a> and <a href="http://www.catalinzima.com/xna/tutorials/deferred-rendering-in-xna/point-lights/" rel="nofollow noreferrer">here (spherical light volumes)</a>.</p>
  
  <p><img src="https://i.stack.imgur.com/6iQfJ.png" alt="position_reconstruction"></p>
  
  <hr>
  
  <h2>Banding</h2>
  
  <p>The reason I ask is because the results I get from deferred vs forward for light attenuation are different.</p>
  
  <p><strong>Deferred</strong>
  <img src="https://i.stack.imgur.com/f1JkU.png" alt="deferred"></p>
  
  <p><strong>Forward</strong>
  <img src="https://i.stack.imgur.com/VMStN.png" alt="forward"></p>
  
  <p>Attenuation is linked to my camera space position as I calculate attenuation as follows:</p>
  
  <pre><code>vec3 light_dir_to = curr_light.camera_space_position - surface_pos_cam;
  float light_dist_sq = dot(light_dir_to, light_dir_to);
  
  float light_attenuation_factor = 1.0f - ((1.0f / (curr_light.radius * curr_light.radius)) * light_dist_sq);
  light_attenuation_factor = clamp(light_attenuation_factor, 0.0f, 1.0f);
  light_attenuation_factor = pow(light_attenuation_factor, curr_light.falloff);
  </code></pre>
  
  <p>The difference isn't super noticeable in these instances, but the instance I try to scale the light (ex. raise it to a power to make it fade out faster), the effects become immediately apparent.</p>
  
  <p><strong>light_atten = pow(light_atten, 2.0f)</strong></p>
  
  <p><img src="https://i.stack.imgur.com/9HcZR.png" alt="attenpow2"></p>
  
  <p>My problem may lie elsewhere, but I want to verify that my position reconstruction method isn't flawed in some way I'm overlooking.</p>
  
  <hr>
  
  <h2>EDIT</h2>
  
  <p>Posting my gbuffer setup as requested.</p>
  
  <pre><code>enum render_targets { e_dist_32f = 0, e_diffuse_rgb8, e_norm_xyz10, e_spec_intens_b8_spec_pow_a8, e_light_rgb8, num_rt };
  //...
  GLint internal_formats[num_rt] = {  GL_R32F, GL_RGBA8, GL_RGB10_A2, GL_RGBA8, GL_RGBA8 };
  GLint formats[num_rt]          = {   GL_RED,  GL_RGBA,     GL_RGBA,  GL_RGBA,  GL_RGBA };
  GLint types[num_rt]            = { GL_FLOAT, GL_FLOAT,    GL_FLOAT, GL_FLOAT, GL_FLOAT };
  for(uint i = 0; i &lt; num_rt; ++i)
  {
    glBindTexture(GL_TEXTURE_2D, _render_targets[i]);
    glTexImage2D(GL_TEXTURE_2D, 0, internal_formats[i], _width, _height, 0, formats[i], types[i], nullptr);
  }
  // Separate non-linear depth buffer used for depth testing
  glBindTexture(GL_TEXTURE_2D, _depth_tex_id);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, _width, _height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
  </code></pre>
  
  <p><strong>NOTE:</strong> This issue occurs on planar surfaces that have one normal for the whole surface, thus this cannot be a loss of precision with normals.</p>
  
  <hr>
  
  <h2>FINAL EDIT - SOLUTION</h2>
  
  <p>It appears as though this method is in-fact valid (as mentioned by GuyRT). The banding issue appears to be coming from how I am doing gamma correction.</p>
  
  <p>For my forward renderer I only have one loop over 8 lights (I don't do multiple passes, 1 pass only), and I apply gamma correction right after the lighting calculations.</p>
  
  <p>For my deferred renderer I do all lighting calculations, post-processing, etc., then convert to gamma. The issue here is that I:</p>
  
  <ol>
  <li>Do my lighting calculations in linear RGB space</li>
  <li>Store it in a texture in RGB space <strong>(with only 8 bits of precision)</strong></li>
  <li>When lighting is done, gamma correct the value and copy it to the back buffer.</li>
  </ol>
  
  <p>For example, let's say the lighting calculations for two fragments have the final values 1/255 (~0.003) and 2/255 (~0.007) in sRGB space (as presented in the end). These values in RGB space are (1/255)^2.2 = ~0.000006 and (2/255)^2.2 = ~0.00002. When these values are stored to my lighting accumulating texture, they are both stored as the same value, 0. This is the cause of the banding.</p>
  
  <p>Converting my lighting accumulation texture to <strong>GL_R11F_G11F_B10F</strong> has yielded results that are very close to my forward renderer. The answers for these two questions helped me once I found that gamma was the issue: <a href="https://stackoverflow.com/a/10348719/2507444">sRGB textures. Is this correct?</a> and <a href="https://stackoverflow.com/a/11386809/2507444">When to call glEnable(GL_FRAMEBUFFER_SRGB)?</a>.</p>
  
  <p>The final result with a "falloff" of 4.0</p>
  
  <p><img src="https://i.stack.imgur.com/Pm2qU.png" alt="final_result"></p>
  
  <hr>
  
  <h2>EXTRA RESOURCE</h2>
  
  <p>I just found out this effect is called "Gamma Banding", which makes sense. This <a href="http://www.graphics.cornell.edu/~westin/gamma/gamma.html" rel="nofollow noreferrer">website has some useful charts</a> and <a href="https://www.udacity.com/course/viewer#!/c-cs291/l-124106597/e-176585831/m-176585832" rel="nofollow noreferrer">this video has a nice numerical walkthrough</a>.</p>