まず、 を使用したくありませんgluLookAt
。gluLookAt
カメラは回転しますが、ユーザーが見ている物理画面は回転しません。gluLookAt
画面法線がユーザーを指し続けるように画面が回転する場合にのみ機能します。軸外投影の遠近歪みは、必要なすべての回転を処理します。
モデルに考慮する必要があるのは、錐台内のスクリーンの位置です。次の画像を考えてみましょう。赤い点は画面の境界です。実現する必要があるのは、これらの位置が 3D WCS で一定のままであることです。これは、現実世界の物理画面も (できれば) 動かないためです。これが仮想現実と立体視の重要な洞察だと思います。スクリーンは仮想現実への窓のようなもので、現実世界を仮想現実に合わせるには、錐台をその窓に合わせる必要があります。
そのためには、Kinect の座標系で画面の位置を決定する必要があります。Kinect が画面の上にあり、+y が下向きで、使用している単位がミリメートルであると仮定すると、これらの座標は (+-300, 200, 0), ( +-300、500、0)。
ここで、ファー プレーンには 2 つの可能性があります。カメラからファー プレーンまでの固定距離を使用することもできます。これは、ユーザーが後方に移動すると、ファー プレーンが後方に移動し、描画したいオブジェクトがクリッピングされる可能性があることを意味します。または、画像に示すように、遠平面を WCS の固定位置に保持することもできます。後者の方が便利だと思います。ニアプレーンについては、カメラからの固定距離は大丈夫だと思います。
入力は、スクリーンwcsPtTopLeftScreen
との 3D 位置wcsPtBottomRightScreen
、追跡された頭の位置wcsPtHead
、ファー プレーンの z 値wcsZFar
(すべて WCS 内)、およびニア プレーンの z 値camZNear
(カメラ座標内) です。カメラ座標で視錐台パラメーターを計算する必要があります。
camPtTopLeftScreen = wcsPtTopLeftScreen - wcsPtHead;
camPtTopLeftNear = camPtTopLeftScreen / camPtTopLeftScreen.z * camZNear;
右下の点も同様です。また:
camZFar = wcsZFar - wcsPtHead.z
唯一の問題は、Kinect と OpenGL が異なる座標系を使用していることです。Kinect CS では、+y は下を指し、+z はユーザーから Kinect に向かって指します。OpenGL では、+y は上向き、+z はビューアの方を向きます。つまり、y と z に -1 を掛ける必要があります。
glFrustum(camPtTopLeftNear.x, camPtBottomRightNear.x,
-camPtBottomRightNear.y, -camPtTopLeftNear.y, camZNear, camZFar);
立体視もカバーするより良い説明が必要な場合は、このビデオをチェックしてください。洞察力があり、よくできています。
簡単なデモです。 wcsWidth
、pxWidth
、およびを調整する必要がある場合がありwcsPtHead.z
ます。
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glut.h>
#include <functional>
float heightFromWidth;
glm::vec3 camPtTopLeftNear, camPtBottomRightNear;
float camZNear, camZFar;
glm::vec3 wcsPtHead(0, 0, -700);
void moveCameraXY(int pxPosX, int pxPosY)
{
// Width of the screen in mm and in pixels.
float wcsWidth = 520.0;
float pxWidth = 1920.0f;
float wcsHeight = heightFromWidth * wcsWidth;
float pxHeight = heightFromWidth * pxWidth;
float wcsFromPx = wcsWidth / pxWidth;
glm::vec3 wcsPtTopLeftScreen(-wcsWidth/2.f, -wcsHeight/2.f, 0);
glm::vec3 wcsPtBottomRightScreen(wcsWidth/2.f, wcsHeight/2.f, 0);
wcsPtHead = glm::vec3(wcsFromPx * float(pxPosX - pxWidth / 2), wcsFromPx * float(pxPosY - pxHeight * 0.5f), wcsPtHead.z);
camZNear = 1.0;
float wcsZFar = 500;
glm::vec3 camPtTopLeftScreen = wcsPtTopLeftScreen - wcsPtHead;
camPtTopLeftNear = camZNear / camPtTopLeftScreen.z * camPtTopLeftScreen;
glm::vec3 camPtBottomRightScreen = wcsPtBottomRightScreen - wcsPtHead;
camPtBottomRightNear = camPtBottomRightScreen / camPtBottomRightScreen.z * camZNear;
camZFar = wcsZFar - wcsPtHead.z;
glutPostRedisplay();
}
void moveCameraZ(int button, int state, int x, int y)
{
// No mouse wheel in GLUT. :(
if ((button == 0) || (button == 2))
{
if (state == GLUT_DOWN)
return;
wcsPtHead.z += (button == 0 ? -1 : 1) * 100;
glutPostRedisplay();
}
}
void reshape(int w, int h)
{
heightFromWidth = float(h) / float(w);
glViewport(0, 0, w, h);
}
void drawObject(std::function<void(GLdouble)> drawSolid, std::function<void(GLdouble)> drawWireframe, GLdouble size)
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
glEnable(GL_COLOR);
glDisable(GL_LIGHTING);
glColor4f(1, 1, 1, 1);
drawSolid(size);
glColor4f(0.8, 0.8, 0.8, 1);
glDisable(GL_DEPTH_TEST);
glLineWidth(1);
drawWireframe(size);
glColor4f(0, 0, 0, 1);
glEnable(GL_DEPTH_TEST);
glLineWidth(3);
drawWireframe(size);
glPopAttrib();
}
void display(void)
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// In the Kinect CS, +y points down, +z points from the user towards the Kinect.
// In OpenGL, +y points up, +z points towards the viewer.
glm::mat4 mvpCube;
mvpCube = glm::frustum(camPtTopLeftNear.x, camPtBottomRightNear.x,
-camPtBottomRightNear.y, -camPtTopLeftNear.y, camZNear, camZFar);
mvpCube = glm::scale(mvpCube, glm::vec3(1, -1, -1));
mvpCube = glm::translate(mvpCube, -wcsPtHead);
glMatrixMode(GL_MODELVIEW); glLoadMatrixf(glm::value_ptr(mvpCube));
drawObject(glutSolidCube, glutWireCube, 140);
glm::mat4 mvpTeapot = glm::translate(mvpCube, glm::vec3(100, 0, 200));
mvpTeapot = glm::scale(mvpTeapot, glm::vec3(1, -1, -1)); // teapots are in OpenGL coordinates
glLoadMatrixf(glm::value_ptr(mvpTeapot));
glColor4f(1, 1, 1, 1);
drawObject(glutSolidTeapot, glutWireTeapot, 50);
glFlush();
glPopAttrib();
}
void leave(unsigned char, int, int)
{
exit(0);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutCreateWindow("glut test");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
moveCameraXY(0,0);
glutPassiveMotionFunc(moveCameraXY);
glutMouseFunc(moveCameraZ);
glutKeyboardFunc(leave);
glutFullScreen();
glutMainLoop();
return 0;
}
次の画像は、画面上の幅の 135% に等しい距離から表示する必要があります (全画面表示の幅 52 cm の画面では 70 cm)。