title: Antialiasing during depth post-processing for SSAO
body: <p>I need to enhance the visual perception of some mechanical parts with very fine details, so I am playing now with different implementations of screen.space ambient occlusion.</p>
  
  <p>Until now, I was drawing my geometries directly to the screen in forward-rendering by using <code>gl.TRIANGLES</code> at double the canvas resolution with antialiasing <em>on</em>, i.e. for instance, let say my canvas style is 800x600 px, then my canvas width/height are set to 1600x1200. Only by using screen antialiasing together with such a high resolution I can get the visual quality which I need.</p>
  
  <p>By using a renderbuffer I am not able to get close to that needed quality, even by rendering at double the resolution, because of the lack of the antialiasing. I tried to implements many different antialiasing techniques in an additional post-processing step, but anyway I am not able to get clean, smooth lines. Here is an example of what I mean: see the long, slightly rotated parts at the bottom of the image.</p>
  
  <p><a href="https://i.stack.imgur.com/BrZUN.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BrZUN.png" alt="antialiasing missed in renderbuffer"></a></p>
  
  <p>Is there any method to get clean, antialiased lines by drawing in a backbuffer during a screen-space post-processing? Any additional hint about other strategies/techniques will be appreciated.</p>
  
  <p>Has someone successfully implemented - just to mention an example - FXAA, or something like that, during the SSAO pass, to get smooth antialiased long diagonal lines, without jagging?</p>
  
  <hr>
  
  <p>Here is the fragment shader related to that picture above:</p>
  
  <pre><code>float compareDepths(in float depth1,in float depth2, in float aoMultiplier) {
      float aoCap = 1.0;
      float diff = sqrt( clamp(1.0-(depth1-depth2) / (u_aoRange/(u_zFar-u_zNear)),0.0,1.0) );
      float ao = min(aoCap,max(0.0, depth1 - depth2 - u_depthTolerance) * aoMultiplier) * diff;
      return ao;
  }
  void main(void) {
      vec2 UV = v_texCoord.st;
      float depth = readDepth(UV);
      float d;
      float pw = 1.0 / u_resolution.x;
      float ph = 1.0 / u_resolution.y;
      float aoCap = 1.0;
      float ao = 0.0;
      float aoMultiplier = u_aoMultiplier;
  
      // 4 samples w/out loop
      float aoscale=1.0;
      d=readDepth( vec2(UV.x+pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x+pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
  
      pw*=2.0;
      ph*=2.0;
      aoMultiplier/=2.0;
      aoscale*=1.2;
      d=readDepth( vec2(UV.x+pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x+pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
  
      pw*=2.0;
      ph*=2.0;
      aoMultiplier/=2.0;
      aoscale*=1.2;
      d=readDepth( vec2(UV.x+pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x+pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
  
      pw*=2.0;
      ph*=2.0;
      aoMultiplier/=2.0;
      aoscale*=1.2;
      d=readDepth( vec2(UV.x+pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y+ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x+pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
      d=readDepth( vec2(UV.x-pw,UV.y-ph));
      ao+=compareDepths(depth, d, aoMultiplier)/aoscale;
  
      ao/=16.0;',
      gl_FragColor = vec4( vec3(1.0-ao), 1.0 );
  }
  </code></pre>
  
  <p><strong>EDIT:</strong></p>
  
  <p>Below I am using the SSAO technique described by John Chapman in this great <a href="http://john-chapman-graphics.blogspot.it/2013/01/ssao-tutorial.html" rel="nofollow noreferrer">SSAO Tutorial</a>.</p>
  
  <p>Left at quadruple resolution, right at full resolution with separated gaussian blur.</p>
  
  <p><a href="https://i.stack.imgur.com/hqs1w.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hqs1w.png" alt="comparison"></a></p>
  
  <p>Please note, the artifacts aren't disturbing by using some classic more "organic" or "curved" models like, for example, the <em>Teapot</em>, the <em>Stanford Dragon</em> or the <em>Happy Buddha</em>. These artifacts are noticeable with regular, geometrical long objects, typical mechanical or architectural shapes.</p>
  
  <p>Is there any method to enhance the depth perception, by keeping that quality and preserving high frequency details, without using four times the canvas resolution?</p>
  
  <p><strong>EDIT2:</strong></p>
  
  <p>The final result looks very good at full canvas size on retina displays, for example on iPad, which has 264 pixels per inch resolution. However, the jagged lines are clearly visible and disturbing on desktop monitors, with typically 70 or 92 DPI.</p>
  
  <p>I discovered this post: <a href="http://www.iquilezles.org/www/articles/multiresaocc/multiresaocc.htm" rel="nofollow noreferrer">multiresolution ambient occlusion</a> from Íñigo Quílez which contain some hints also about <strong>High frequency occlusion - procedural occlusion</strong> to enhance tiny details, but I am not able to understand if this could be applicable also for straight geometrical shapes. Someone did already experiments with this?</p>
  
  <p><strong>EDIT3:</strong></p>
  
  <p>here is the most useful reference I found so far, about this topic: <a href="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter22.html" rel="nofollow noreferrer">Fast Prefiltered Lines</a> from <em>GPU Gems 2</em>.
  Someone already implemented the technique described here together with SSAO?</p>