title: Low resolution render to texture result when using perspective projection
body: <p>I'm currently working on a camera app for the iPhone in which I take the camera input, convert that to an OpenGL texture and then map it onto a 3D Object (currently a plane in perspective projection, for the sake of simplicity). 
  After mapping the camera input to this 3D plane I then render this 3D scene to a texture which is then used as a new texture for a plane in orthographic space (to apply additional filters in my fragment shader).</p>
  
  <p>As long as I keep everything in orthographic projection, the resolution of my render texture is pretty high. But from the moment I put my plane in perspective projection the resolution of my render texture is very low.</p>
  
  <p>Comparison:</p>
  
  <p><img src="https://i.stack.imgur.com/zMpNy.jpg" alt="Resolution comparison"></p>
  
  <p>As you can see, the last image has a very low resolution compared to the other two. So I'm guessing I'm doing something wrong.</p>
  
  <p>I'm currently not using multisampling on any of my framebuffers and I'm in doubt if I will need it anyway to fix my problem since the orthographic scene works perfectly.</p>
  
  <p>The textures I render into are 2048x2048 (will eventually be outputted as an image to the iPhone camera roll).</p>
  
  <p>Here are some parts of my source code that I think might be relevant:</p>
  
  <p>Code to create the framebuffer that gets outputted to the screen:</p>
  
  <pre><code>// Color renderbuffer
  glGenRenderbuffers(1, &amp;colorRenderBuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
  [context renderbufferStorage:GL_RENDERBUFFER 
                   fromDrawable:(CAEAGLLayer*)glView.layer];
  
  // Depth renderbuffer
  glGenRenderbuffers(1, &amp;depthRenderbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
  
  // Framebuffer
  glGenFramebuffers(1, &amp;defaultFrameBuffer);
  glBindFramebuffer(GL_FRAMEBUFFER, defaultFrameBuffer);
  
  // Associate renderbuffers with framebuffer
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
        GL_RENDERBUFFER, colorRenderBuffer);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 
        GL_RENDERBUFFER, depthRenderbuffer);
  </code></pre>
  
  <p>TextureRenderTarget class:</p>
  
  <pre><code>void TextureRenderTarget::init()
  {
      // Color renderbuffer
      glGenRenderbuffers(1, &amp;colorRenderBuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8_OES, 
             width, height);
  
      // Depth renderbuffer
      glGenRenderbuffers(1, &amp;depthRenderbuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 
             width, height);
  
      // Framebuffer
      glGenFramebuffers(1, &amp;framebuffer);
      glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  
      // Associate renderbuffers with framebuffer
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
            GL_RENDERBUFFER, colorRenderBuffer);
      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 
            GL_RENDERBUFFER, depthRenderbuffer);
  
      // Texture and associate with framebuffer
      texture = new RenderTexture(width, height);
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
            GL_TEXTURE_2D, texture-&gt;getHandle(), 0);
  
      // Check for errors
      checkStatus();
  }
  
  void TextureRenderTarget::bind() const
  {
      glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
      glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
  }
  
  
  
  void TextureRenderTarget::unbind() const
  {
      glBindFramebuffer(GL_FRAMEBUFFER, 0);
      glBindRenderbuffer(GL_RENDERBUFFER, 0);
  }
  </code></pre>
  
  <p>And finally, a snippet on how I create the render texture and fill it with pixels:</p>
  
  <pre><code>void Texture::generate()
  {
      // Create texture to render into
      glActiveTexture(unit);
      glGenTextures(1, &amp;handle);
      glBindTexture(GL_TEXTURE_2D, handle);
  
      // Configure texture
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }
  
  void Texture::setPixels(const GLvoid* pixels)
  {
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 
           GL_UNSIGNED_BYTE, pixels);
      updateMipMaps();
  }
  
  void Texture::updateMipMaps() const
  {
      glBindTexture(GL_TEXTURE_2D, handle);
      glGenerateMipmap(GL_TEXTURE_2D);
  }
  
  void Texture::bind(GLenum unit)
  {
      this-&gt;unit = unit;
  
      if(unit != -1)
      {
          glActiveTexture(unit);
          glBindTexture(GL_TEXTURE_2D, handle);
      }
      else
      {
          cout &lt;&lt; "Texture::bind -&gt; Couldn't activate unit -1" &lt;&lt; endl;
      }
  }
  
  void Texture::unbind()
  {
      glBindTexture(GL_TEXTURE_2D, 0);    
  }
  </code></pre>