title: Incorrect depthTexture with SSAO
body: <p>I’ve been puzzled lately as I’ve been attempting to get a <code>THREE.DepthTexture</code> to work with the Ambient Occlusion shader. I’ve had it working before with RGBA unpacking, but after reading about Matt Deslauriers’s project, Audiograph, I decided to attempt the method he described for a potential performance boost:</p>
  
  <blockquote>
    <p>Historically in ThreeJS, you would render your scene with
    MeshDepthMaterial to a WebGLRenderTarget, and then unpack to a linear
    depth value when sampling from the depth target. This is fairly
    expensive and often unnecessary, since many environments support the
    WEBGL_depth_texture extension.</p>
  </blockquote>
  
  <ul>
  <li><a href="https://mattdesl.svbtle.com/audiograph" rel="nofollow noreferrer">Matt Deslauriers, Audiograph</a></li>
  </ul>
  
  <p>After attempting this method, I somehow ended up with this weird unwanted effect in which lines are all over the terrain:</p>
  
  <p><a href="https://i.stack.imgur.com/KIRQO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/KIRQO.png" alt="Lines over terrain"></a></p>
  
  <p>I have setup a small example below in which I have replicated the issue. I feel it’s something very obvious that I’m simply glossing over.</p>
  
  <p>I hope someone here is able to point out what I’m missing so that I can get the ambient occlusion working in a way that is a little bit more performant!</p>
  
  <p>Many thanks in advance.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="true">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 2000);
  
  const pivot = new THREE.Object3D();
  pivot.add(camera);
  scene.add(pivot);
  
  camera.position.set(0, 250, 500);
  camera.lookAt(pivot.position);
  
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.gammaInput = true;
  renderer.gammaOutput = true;
  renderer.gammaFactor = 2.2;
  
  let supportsExtension = false;
  
  if (renderer.extensions.get('WEBGL_depth_texture')) {
    supportsExtension = true;
  }
  
  document.body.appendChild(renderer.domElement);
  
  const createCube = () =&gt; {
   const geo = new THREE.BoxGeometry(500, 500, 500);
    const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const obj = new THREE.Mesh(geo, mat);
    obj.position.y = -(obj.geometry.parameters.height / 2);
    scene.add(obj);
  }
  
  const createSphere = () =&gt; {
   const geo = new THREE.SphereGeometry(100, 12, 8);
    const mat = new THREE.MeshBasicMaterial({ color: 0xff00ff });
    const obj = new THREE.Mesh(geo, mat);
    obj.position.y = obj.geometry.parameters.radius;
    scene.add(obj);
  }
  
  // Create objects
  createCube();
  createSphere();
  
  const composer = new THREE.EffectComposer(renderer);
  
  const target = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
  target.texture.format = THREE.RGBFormat;
  target.texture.minFilter = THREE.NearestFilter;
  target.texture.magFilter = THREE.NearestFilter;
  target.texture.generateMipmaps = false;
  target.stencilBuffer = false;
  target.depthBuffer = true;
  target.depthTexture = new THREE.DepthTexture();
  target.depthTexture.type = THREE.UnsignedShortType;
  
  function initPostProcessing() {
   composer.addPass(new THREE.RenderPass( scene, camera ));
  
    const pass = new THREE.ShaderPass({
     uniforms: {
       "tDiffuse":     { value: null },
       "tDepth":       { value: target.depthTexture },
       "resolution":   { value: new THREE.Vector2( 512, 512 ) },
       "cameraNear":   { value: 1 },
       "cameraFar":    { value: 100 },
       "onlyAO":       { value: 0 },
       "aoClamp":      { value: 0.5 },
       "lumInfluence": { value: 0.5 }
     },
     vertexShader: document.getElementById('vertexShader').textContent,
     fragmentShader: document.getElementById('fragmentShader').textContent,
    });
   pass.material.precision = 'highp';
    composer.addPass(pass);
    pass.uniforms.tDepth.value = target.depthTexture;
    pass.uniforms.cameraNear.value = camera.near;
    pass.uniforms.cameraFar.value = camera.far;
    
    composer.passes[composer.passes.length - 1].renderToScreen = true;
  }
  
  initPostProcessing();
  
  const animate = () =&gt; {
   requestAnimationFrame( animate );
  
   pivot.rotation.y += 0.01;
  
    renderer.render( scene, camera, target );
  
    composer.render();
  }
  
  animate();</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>html, body { margin: 0; }
  canvas { display: block; width: 100%; height: 100%; }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js"&gt;&lt;/script&gt;
  &lt;script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/EffectComposer.js"&gt;&lt;/script&gt;
  &lt;script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/RenderPass.js"&gt;&lt;/script&gt;
  &lt;script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/postprocessing/ShaderPass.js"&gt;&lt;/script&gt;
  &lt;script src="https://cdn.rawgit.com/mrdoob/three.js/dev/examples/js/shaders/CopyShader.js"&gt;&lt;/script&gt;
  
  &lt;script id="vertexShader" type="x-shader/x-vertex"&gt;
   varying vec2 vUv;
    
    void main() {
     
      vUv = uv;
      
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      
    }
  &lt;/script&gt;
  &lt;script id="fragmentShader" type="x-shader/x-fragment"&gt;
  uniform float cameraNear;
  uniform float cameraFar;
  
  uniform bool onlyAO;      // use only ambient occlusion pass?
  
  uniform vec2 resolution;        // texture width, height
  uniform float aoClamp;    // depth clamp - reduces haloing at screen edges
  
  uniform float lumInfluence;  // how much luminance affects occlusion
  
  uniform sampler2D tDiffuse;
  uniform highp sampler2D tDepth;
  
  varying vec2 vUv;
  
  // #define PI 3.14159265
  #define DL 2.399963229728653  // PI * ( 3.0 - sqrt( 5.0 ) )
  #define EULER 2.718281828459045
  
  // user variables
  
  const int samples = 4;     // ao sample count
  const float radius = 5.0;  // ao radius
      
  const bool useNoise = false;      // use noise instead of pattern for sample dithering
  const float noiseAmount = 0.0003; // dithering amount
  
  const float diffArea = 0.4;   // self-shadowing reduction
  const float gDisplace = 0.4;  // gauss bell center
  
  highp vec2 rand( const vec2 coord ) {
  
    highp vec2 noise;
  
      if ( useNoise ) {
  
        float nx = dot ( coord, vec2( 12.9898, 78.233 ) );
        float ny = dot ( coord, vec2( 12.9898, 78.233 ) * 2.0 );
  
        noise = clamp( fract ( 43758.5453 * sin( vec2( nx, ny ) ) ), 0.0, 1.0 );
  
      } else {
  
        highp float ff = fract( 1.0 - coord.s * ( resolution.x / 2.0 ) );
        highp float gg = fract( coord.t * ( resolution.y / 2.0 ) );
  
        noise = vec2( 0.25, 0.75 ) * vec2( ff ) + vec2( 0.75, 0.25 ) * gg;
  
      }
  
      return ( noise * 2.0  - 1.0 ) * noiseAmount;
  
    }
  
   float readDepth( const in vec2 coord ) {
  
     float cameraFarPlusNear = cameraFar + cameraNear;
    float cameraFarMinusNear = cameraFar - cameraNear;
    float cameraCoef = 2.0 * cameraNear;
  
     return cameraCoef / ( cameraFarPlusNear - texture2D( tDepth, coord ).x * cameraFarMinusNear );
  
   }
  
    float compareDepths( const in float depth1, const in float depth2, inout int far ) {
  
      float garea = 2.0;                         // gauss bell width
      float diff = ( depth1 - depth2 ) * 100.0;  // depth difference (0-100)
  
      // reduce left bell width to avoid self-shadowing
  
      if ( diff &lt; gDisplace ) {
  
        garea = diffArea;
  
      } else {
  
        far = 1;
  
      }
  
        float dd = diff - gDisplace;
        float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );
        return gauss;
  
      }
  
      float calcAO( float depth, float dw, float dh ) {
  
        float dd = radius - depth * radius;
        vec2 vv = vec2( dw, dh );
  
        vec2 coord1 = vUv + dd * vv;
        vec2 coord2 = vUv - dd * vv;
  
        float temp1 = 0.0;
        float temp2 = 0.0;
  
        int far = 0;
        temp1 = compareDepths( depth, readDepth( coord1 ), far );
  
        // DEPTH EXTRAPOLATION
  
        if ( far &gt; 0 ) {
  
          temp2 = compareDepths( readDepth( coord2 ), depth, far );
          temp1 += ( 1.0 - temp1 ) * temp2;
  
        }
  
        return temp1;
  
      }
  
      void main() {
  
        highp vec2 noise = rand( vUv );
        float depth = readDepth( vUv );
        float tt = clamp( depth, aoClamp, 1.0 );
  
        float w = ( 1.0 / resolution.x )  / tt + ( noise.x * ( 1.0 - noise.x ) );
        float h = ( 1.0 / resolution.y ) / tt + ( noise.y * ( 1.0 - noise.y ) );
  
        float ao = 0.0;
  
        float dz = 1.0 / float( samples );
        float z = 1.0 - dz / 2.0;
        float l = 0.0;
  
        for ( int i = 0; i &lt;= samples; i ++ ) {
  
          float r = sqrt( 1.0 - z );
  
          float pw = cos( l ) * r;
          float ph = sin( l ) * r;
          ao += calcAO( depth, pw * w, ph * h );
          z = z - dz;
          l = l + DL;
  
        }
  
        ao /= float( samples );
        ao = 1.0 - ao;
  
        vec3 color = texture2D( tDiffuse, vUv ).rgb;
  
        vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );
        float lum = dot( color.rgb, lumcoeff );
        vec3 luminance = vec3( lum );
  
        vec3 final = vec3( color * mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );  // mix( color * ao, white, luminance )
       float depth2 = readDepth(vUv);
          
      if ( onlyAO ) {
  
        final = vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );  // ambient occlusion only
  
      }
   
   // gl_FragColor = vec4( vec3( readDepth( vUv) ), 1.0 ); // Depth
     gl_FragColor = vec4( final, 1.0 );
  
  }
  &lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>I'd love to hear what is causing my Ambient Occlusion to not render properly!</p>