Po pierwsze, nie chcesz używać gluLookAt
. gluLookAt
obraca kamerę, ale fizyczny ekran, na który patrzy użytkownik, nie obraca się. gluLookAt
działałby tylko wtedy, gdyby ekran był obrócony tak, aby normalny ekran nadal wskazywał użytkownika. Perspektywiczne zniekształcenie projekcji poza osią zajmie się wszystkimi potrzebnymi obrotami.
To, co musisz uwzględnić w swoim modelu, to pozycja ekranu w ciele ściętym. Rozważ następujący obraz. Czerwone punkty to granice ekranu. To, co musisz osiągnąć to to, że te pozycje pozostają stałe w 3D WCS, ponieważ fizyczny ekran w realnym świecie również (mam nadzieję) nie porusza się. Myślę, że to kluczowy wgląd w rzeczywistość wirtualną i stereoskopię. Ekran jest czymś w rodzaju okna wirtualnej rzeczywistości, a aby dopasować rzeczywisty świat do rzeczywistości wirtualnej, trzeba wyrównać tę płaszczyznę z tym oknem.
Aby to zrobić trzeba określić położenie ekranu w układzie współrzędnych Kinect. Zakładając, że Kinect znajduje się na górze ekranu, to + y wskazuje w dół, a urządzenie, z którego korzystasz, to milimetry, spodziewam się, że współrzędne te będą równe (+ -300, 200, 0), (+ -300, 500, 0).
Istnieją teraz dwie możliwości dla dalekiego samolotu. Możesz wybrać użycie stałej odległości od kamery do dalekiej płaszczyzny. Oznaczałoby to, że daleki samolot cofnąłby się, gdyby użytkownik cofnął się, prawdopodobnie przycinając obiekty, które chciałbyś narysować.Lub możesz utrzymać daleką płaszczyznę w ustalonej pozycji w WCS, jak pokazano na obrazku. Uważam, że ten drugi jest bardziej przydatny. Jeśli chodzi o najbliższy samolot, myślę, że ustalona odległość od kamery jest w porządku.
Wejścia są położenia 3D ekranu wcsPtTopLeftScreen
i wcsPtBottomRightScreen
, śledzonym położenie głowicy wcsPtHead
, wartość Z z drugiej płaszczyźnie wcsZFar
(wszystko w WCS), i wartość Rz od najbliższej powierzchni camZNear
(we współrzędnych kamery). Musimy obliczyć parametry punktu ściętego we współrzędnych kamery.
camPtTopLeftScreen = wcsPtTopLeftScreen - wcsPtHead;
camPtTopLeftNear = camPtTopLeftScreen/camPtTopLeftScreen.z * camZNear;
i tym samym z dolnym prawym punktem. Też:
camZFar = wcsZFar - wcsPtHead.z
Teraz jedynym problemem jest to, że Kinect i OpenGL stosować różne układy współrzędnych. W modelu Kinect CS, + y punktów w dół, + punktów z użytkownika w kierunku urządzenia Kinect. W OpenGL + y wskazuje w górę, + z wskazuje na widza. Oznacza to, że musimy pomnożyć przez Y i Z -1:
glFrustum(camPtTopLeftNear.x, camPtBottomRightNear.x,
-camPtBottomRightNear.y, -camPtTopLeftNear.y, camZNear, camZFar);
Jeśli chcesz lepsze wyjaśnienie, które obejmuje również stereoskopii, sprawdź this video, znalazłem to wnikliwe i dobrze zrobione.
Szybkie demo, konieczne może być dostosowanie wcsWidth
, pxWidth
i 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;
}
następujących obrazów powinno być oglądany z odległości równej 135% ich szerokość na ekranie (70 cm na 52 cm mi szerokości w pełnym ekranie).
Ciekawy projekt. Zastanawiam się, jakie są szanse na efekt końcowy, że dzieci siedzą cal od telewizora, aby uzyskać maksymalny widok świata? ;) –
Czy udało ci się go uruchomić z Kinect? Film z perspektywy użytkownika byłby naprawdę fajny. –