9

アプリケーションで軸外投影を実行しようとしており、ユーザーの頭の位置に応じてシーンの視点を変更しようとしています。通常、画面にボックスを描画する必要がある場合、次のようにボックスを画面に描画します。

ofBox(350,250,0,50); //ofBox(x, y, z, size); where x, y and z used here are the screen coordinates

ここで軸外投影を行うには、透視投影を次のように変更する必要があることを認識しています。

vertFov = 0.5; near = 0.5; aspRatio = 1.33;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(near * (-vertFov * aspRatio + headX),
          near * (vertFov * aspRatio + headX),
          near * (-vertFov + headY),
          near * (vertFov + headY),
          near, far); //frustum changes as per the position of headX and headY
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(headX * headZ, headY * headZ, 0, headX * headZ, headY * headZ, -1);
glTranslate(0,0,headZ);

上記の場合 (headX と headY がゼロの場合) の対称錐台の場合、パラメータはleft, rightになり-0.33、パラメータは になり、それらの座標に沿ってクリッピング ボリュームを確立します。テスト用にマウスを使用して軸外をシミュレートしようとしましたが、次のことを行いました。0.33bottom, top-0.25, 0.25

double mouseXPosition = (double)ofGetMouseX();
double mouseYPosition = (double)ofGetMouseY();
double scrWidth = (double)ofGetWidth();
double scrHeight = (double)ofGetHeight();

headX = ((scrWidth -mouseXPosition) / scrWidth) - 0.5;
headY = (mouseYPosition / scrHeight) - 0.5;
headZ = -0.5; //taken z constant for this mouse test

ただし、、、、(400、100、1400)などKinectの順序の頭の座標を提供するものを使用するつもりであり、それらの頭の位置があるときに錐台パラメータを変更する方法を理解できません。例: Kinect の中心にいることを考慮して、ユーザーが自分の位置がになるように移動した場合、錐台パラメーターはここでどのように変化しますか? 取得元も考慮する必要がある 場合、オブジェクトをどのように描画する必要がありますか? オブジェクトは、増加するにつれてサイズが小さくなる必要があり、それは次の方法で発生する可能性があります(200, 400, 1000)(-250, 600, 1400)0(200, 400, 1000)
z-distanceKinectzglTrasnlate()上記の軸外コード内で呼び出しますが、座標系の 2 つのスケールが異なります (glFrustum はクリッピング ボリュームを [-0.25,0.33] から [0.25,-0.33] に設定します。Kinect は数百のオーダーです(400,200,1000))。glFrustum/ gluLookAtthenに z 値を適用するにはどうすればよいですか?

4

2 に答える 2

8

まず、 を使用したくありませんgluLookAtgluLookAtカメラは回転しますが、ユーザーが見ている物理画面は回転しません。gluLookAt画面法線がユーザーを指し続けるように画面が回転する場合にのみ機能します。軸外投影の遠近歪みは、必要なすべての回転を処理します。

モデルに考慮する必要があるのは、錐台内のスクリーンの位置です。次の画像を考えてみましょう。赤い点は画面の境界です。実現する必要があるのは、これらの位置が 3D WCS で一定の​​ままであることです。これは、現実世界の物理画面も (できれば) 動かないためです。これが仮想現実と立体視の重要な洞察だと思います。スクリーンは仮想現実への窓のようなもので、現実世界を仮想現実に合わせるには、錐台をその窓に合わせる必要があります。

素晴らしい MSPaint スキル

そのためには、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);

立体視もカバーするより良い説明が必要な場合は、このビデオをチェックしてください。洞察力があり、よくできています。

簡単なデモです。 wcsWidthpxWidth、およびを調整する必要がある場合があり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)。 ここに画像の説明を入力 ここに画像の説明を入力

于 2013-05-26T00:54:31.800 に答える
3

ヘッド トラッキング アプリケーションに glFrustum を使用する方法についての最良の説明は、一般化された透視投影と呼ばれる Robert Kooima のこの論文にあります。

http://csc.lsu.edu/~kooima/pdfs/gen-perspective.pdf

また、ステレオ プロジェクションを簡単に使用することもできます。左右のカメラを切り替えるだけです。

于 2013-07-03T14:54:17.617 に答える