title: OpenGL Asymmetric Frustum for Desktop VR
body: <p>I am making an OpenGL c++ application that tracks the users location in relation to the screen and then updates the rendered scene to the perspective of the user. This is know as "desktop VR" or you can think of the screen as a diorama or fish tank. I am rather new to OpenGL and have only defined a very simple scene thus far, just a cube, and it is initially rendered correctly. </p>
  
  <p>The problem is that when I start moving and want to rerender the cube scene, the projection plane seems translated and I don't see what I think I should. I want this plane fixed. If I were writing a ray tracer, my window would always be fixed, but my eye is allowed to wander. Can someone please explain to me how I can achieve the effect I desire (pinning the viewing window) while having my camera/eye wander at a non-origin coordinate? </p>
  
  <p>All of the examples I find demand that the camera/eye be at the origin, but this is not conceptually convenient for me. Also, because this is a "fish tank", I am setting my d_near to be the xy-plane, where z = 0.</p>
  
  <p>In screen/world space, I assign the center of the screen to (0,0,0) and its 4 corners to:
  TL(-44.25, 25, 0)
  TR( 44.25, 25, 0)
  BR( 44.25,-25, 0)
  BL(-44.25,-25, 0)
  These values are in cm for a 16x9 display.</p>
  
  <p>I then calculate the user's eye (actually a web cam on my face) using POSIT to be usually somewhere in the range of (+/-40, +/-40, 40-250). My POSIT method is accurate.</p>
  
  <p>I am defining my own matrices for the perspective and viewing transforms and using shaders.</p>
  
  <p>I initialize as follows:</p>
  
  <pre><code>float right = 44.25;
  float left = -44.25;
  float top = 25.00;
  float bottom = -25.00; 
  
  vec3 eye = vec3(0.0, 0.0, 100.0);
  vec3 view_dir = vec3(0.0, 0.0, -1.0);
  vec3 up = vec3(0.0, 1.0, 0.0);
  vec3 n = normalize(-view_dir);
  vec3 u = normalize(cross(up, n)); 
  vec3 v = normalize(cross(n, u));
  
  float d_x = -(dot(eye, u));
  float d_y = -(dot(eye, v));
  float d_z = -(dot(eye, n));
  
  float d_near = eye.z;
  float d_far = d_near + 50;
  
  // perspective transform matrix
  mat4 P = mat4((2.0*d_near)/(right-left ), 0, (right+left)/(right-left), 0, 
              0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
              0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
              0, 0, -1.0, 0);
  
  // viewing transform matrix
  mat4 V = mat4(u.x, u.y, u.z, d_x,
                v.x, v.y, v.z, d_y,
                n.x, n.y, n.z, d_z,
                0.0, 0.0, 0.0, 1.0);
  
  mat4 MV = C * V;
  //MV = V;
  </code></pre>
  
  <p>From what I gather looking on the web, my view_dir and up are to remain fixed. This means that I need only to update d_near and d_far as well as d_x, d_y, and d_y? I do this in my glutIdleFunc( idle );</p>
  
  <pre><code>void idle (void) {  
  
      hBuffer-&gt;getFrame(hFrame);
      if (hFrame-&gt;goodH &amp;&amp; hFrame-&gt;timeStamp != timeStamp) {
          timeStamp = hFrame-&gt;timeStamp;
          std::cout &lt;&lt; "(" &lt;&lt; hFrame-&gt;eye.x &lt;&lt; ", " &lt;&lt;
                      hFrame-&gt;eye.y &lt;&lt; ", " &lt;&lt;
                      hFrame-&gt;eye.z &lt;&lt; ") \n";
  
          eye = vec3(hFrame-&gt;eye.x, hFrame-&gt;eye.y, hFrame-&gt;eye.z);
  
          d_near = eye.z;
          d_far = eye.z + 50;
  
          P = mat4((2.0*d_near)/(right-left), 0, (right+left)/(right-left), 0, 
                   0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
                   0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
                   0, 0, -1.0, 0);
  
          d_x = -(dot(eye, u));
          d_y = -(dot(eye, v));
          d_z = -(dot(eye, n));
  
          C = mat4(1.0, 0.0, 0.0, eye.x,
                   0.0, 1.0, 0.0, eye.y,
                   0.0, 0.0, 1.0, 0.0,
                   0.0, 0.0, 0.0, 1.0);
  
          V = mat4(u.x, u.y, u.z, d_x,
                   v.x, v.y, v.z, d_y,
                   n.x, n.y, n.z, d_z,
                   0.0, 0.0, 0.0, 1.0);
  
          MV = C * V;
          //MV = V;
  
          glutPostRedisplay();
      }
  }
  </code></pre>
  
  <p>Here is my shader code:</p>
  
  <pre><code>#version 150
  
  uniform mat4 MV;
  uniform mat4 P;
  in vec4 vPosition;
  in vec4 vColor;
  out vec4 color;
  
  void 
  main() 
  { 
      gl_Position = P * MV * vPosition;
      color = vColor;
  }
  </code></pre>
  
  <p>Ok, I made some changes to my code, but without success. When I use V in place of MV in the vertex shader, everything looks as I want it to, the perspective is correct and the objects are the right size, however, the scene is translated by the displacement of the camera. </p>
  
  <p>When using C and V to obtain MV, my scene is rendered from the perspective of an observer straight on and the rendered scene fills the window as it should, but the perspective of the eye/camera is lost. </p>
  
  <p>Really, what I want is to translate the 2D pixels, the projection plane, by the appropriate x and y values of the eye/camera, so as to keep the center of the object (whose xy center is (0,0)) in the center of the rendered image. I am guided by the examples in the textbook "Interactive Computer Graphics: A Top-Down Approach with Shader-Based OpenGL (6th Edition)". Using the files paired with the book freely available on the web, I am continuing with the row major approach.</p>
  
  <p>The following images are taken when not using the matrix C to create MV. When I do use C to create MV, all scenes look like the first image below. I desire no translation in z and so I leave that as 0. </p>
  
  <p>Because the projection plane and my camera plane are parallel, the conversion from one to the other coordinate system is simply a translation and inv(T) ~ -T. </p>
  
  <p>Here is my image for the eye at (0,0,50):
  <img src="https://i.stack.imgur.com/bXQaS.png" alt="Here is my image for the eye at (0,0,50):"></p>
  
  <p>Here is my image for the eye at (56,-16,50):
  <img src="https://i.stack.imgur.com/uLHgd.png" alt="Here is my image for the eye at (56,-16,50):"></p>