title: Strange precision loss of 12bit values within 16bit OpenGL 3D texture
body: <p>I have a 3D volume of values in the range [-1024; 3071]. Lets call it <code>input</code>. I need to process those values with OpenGL in such way, that 1 corresponds to a value of 3071 whereas 0 maps to -1024. Since 12 bits are enough to cover a range of 4096 possible values, I create a <code>GL_TEXTURE_3D</code> object with the internal format <code>GL_INTENSITY16</code>. Before uploading the texture data as an array of <code>uint16_t</code> values, I do perform a left-shift by 4 bits in order to establish the required mapping, as described above:</p>
  
  <pre><code>const std::vector&lt; signed short &gt;&amp; inputBuffer = input.buffer();
  std::vector&lt; uint16_t &gt; buffer( inputBuffer.size() );
  
  for( unsigned int i = 0; i &lt; inputBuffer .size(); ++i )
  {
      buffer[ i ]
          = static_cast&lt; uint16_t &gt;( ( inputBuffer[ i ] + 1024 ) &lt;&lt; 4 );
  }
  
  glBindTexture( GL_TEXTURE_3D, volumeTextureID );
  glTexImage3D( GL_TEXTURE_3D, 0, GL_INTENSITY16
              , size.x, size.y, size.z
              , 0, GL_RED, GL_UNSIGNED_SHORT, &amp;buffer.front() );
  </code></pre>
  
  <p>Then I do sample the values of the texture along a line in x-direction in such steps, that the values supporting points are hit always, meaning that no interpolation is required. In order to perform the sampling, points are drawn to a framebuffer object, which uses the texture object <code>colorBufferID</code> as its <code>GL_RGBA16F</code> color buffer:</p>
  
  <pre><code>float gpuSampleAt( float x, float y, float z )
  {
      bindFramebufferObject();
      glBindTexture( GL_TEXTURE_3D, volumeTextureID );
      glBegin( GL_POINTS );
          glTexCoord3f( x, y, z );
          glVertex3f( 0, 0, 0 );
      glEnd();
      unbindFramebufferObject();
  
      float intensity;
      glBindTexture( GL_TEXTURE_2D, colorBufferID );
      glGetTexImage( GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, &amp;intensity );
      return intensity * 4095 - 1024;
  }
  </code></pre>
  
  <p>As mentioned, the above function is executed for different <code>x</code> values. The intensities obtained are plotted in relation to their <code>x</code> position:</p>
  
  <pre><code>const float ry = y / float( size.y - 1 );
  const float rz = z / float( size.z - 1 );
  for( unsigned int x = 0; x &lt; size.x; ++x )
  {
      const float rx = x / float( size.x - 1 );
      gpuPlot( x, gpuSampleAt( rx, ry, rz ) );
      cpuPlot( x, static_cast&lt; signed short &gt;
          ( buffer[ x + size.x * y + size.y * size.x * z ] &gt;&gt; 4 ) -1024 );
  }
  </code></pre>
  
  <p>The same is done on the CPU, which is quite straightforward. The results of the plot are shown by the figure below -samples obtained by CPU are shown by the blue curve, those obtained via OpenGL are red colored:</p>
  
  <p><img src="https://i.stack.imgur.com/BtnWx.png" alt="Plot of the obtained data - blue: samples obtained by CPU - red: those obtained by OpenGL"></p>
  
  <p>Why aren't the results the same?</p>