Как использовать вектор гравитации, чтобы правильно трансформировать сцену для дополненной реальности? - PullRequest
2 голосов
/ 30 марта 2010

Я пытаюсь выяснить, как заставить указанный объект OpenGL отображаться корректно в соответствии с ориентацией устройства (т.е. в соответствии с вектором гравитации от акселерометра и курсом от компаса).

Пример проекта GLGravity имеет пример, который почти такой (несмотря на игнорирование заголовка), но он имеет некоторые глюки. Например, чайник подскочил на 180 градусов, когда угол обзора устройства пересекает горизонт, и он также резко поворачивается, если вы наклоняете устройство из портретного в горизонтальное положение. Это хорошо для контекста этого приложения, так как оно просто демонстрирует объект и не имеет значения, что оно делает эти вещи. Но это означает, что код просто не работает, когда вы пытаетесь эмулировать просмотр объекта OpenGL в реальной жизни в соответствии с ориентацией устройства. Что происходит, так это то, что он почти работает, но вращение курса, которое вы применяете с компаса, «искажается» ложными дополнительными поворотами, замеченными в примере проекта GLGravity.

Может ли кто-нибудь предоставить пример кода, который показывает, как правильно отрегулировать ориентацию устройства (например, вектор гравитации) или исправить пример GLGravity, чтобы он не включал ложные изменения курса?

//Clear matrix to be used to rotate from the current referential to one based on the gravity vector
bzero(matrix, sizeof(matrix));
matrix[3][3] = 1.0;

//Setup first matrix column as gravity vector
matrix[0][0] = accel[0] / length;
matrix[0][1] = accel[1] / length;
matrix[0][2] = accel[2] / length;

//Setup second matrix column as an arbitrary vector in the plane perpendicular to the gravity vector {Gx, Gy, Gz} defined by by the equation "Gx * x + Gy * y + Gz * z = 0" in which we arbitrarily set x=0 and y=1
matrix[1][0] = 0.0;
matrix[1][1] = 1.0;
matrix[1][2] = -accel[1] / accel[2];
length = sqrtf(matrix[1][0] * matrix[1][0] + matrix[1][1] * matrix[1][1] + matrix[1][2] * matrix[1][2]);
matrix[1][0] /= length;
matrix[1][1] /= length;
matrix[1][2] /= length;

//Setup third matrix column as the cross product of the first two
matrix[2][0] = matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1];
matrix[2][1] = matrix[1][0] * matrix[0][2] - matrix[1][2] * matrix[0][0];
matrix[2][2] = matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];

//Finally load matrix
glMultMatrixf((GLfloat*)matrix);

Вот пояснение, показывающее, как получить угол возвышения и наклона, необходимые для решения gluLookAt, как показано в моем последнем ответе:

// elevation comes from z component (0 = facing horizon)
elevationRadians = asin(gravityVector.z / Vector3DMagnitude(gravityVector));

// tilt is how far screen is from vertical, looking along z axis
tiltRadians = atan2(-gravityVector.y, -gravityVector.x) - M_PI_2;

Вслед за предложением Криса: я не уверен, правильно ли я все понял из-за различных соглашений порядка строк / столбцов и заголовка cw или ccw. Однако следующий код - это то, что я придумал:

Vector3D forward = Vector3DMake(0.0f, 0.0f, -1.0f);

// Multiply it by current rotation matrix to get teapot direction
Vector3D direction;     
direction.x = matrix[0][0] * forward.x + matrix[1][0] * forward.y + matrix[2][0] * forward.z;
direction.y = matrix[0][1] * forward.x + matrix[1][1] * forward.y + matrix[2][1] * forward.z;
direction.z = matrix[0][2] * forward.x + matrix[1][2] * forward.y + matrix[2][2] * forward.z;

heading = atan2(direction.z, direction.x) * 180 / M_PI;

// Use this heading to adjust the teapot direction back to keep it fixed
// Rotate about vertical axis (Y), as it is a heading adjustment
glRotatef(heading, 0.0, 1.0, 0.0);

Когда я запускаю этот код, поведение чайника явно «улучшилось», например. курс больше не переворачивается на 180 градусов, когда экран устройства (в книжной ориентации) наклонен вперед / назад через вертикальное положение. Тем не менее, он все еще делает большие скачки в курсе, когда устройство (в горизонтальной ориентации) наклонено вперед / назад. Так что что-то не так. Это говорит о том, что приведенный выше расчет фактического курса неверен ...

Ответы [ 3 ]

3 голосов
/ 03 апреля 2010

Я наконец нашел решение, которое работает. : -)

Я отбросил подход матрицы вращения и вместо этого принял gluLookAt. Чтобы сделать это, вам нужно знать «высоту» устройства (угол обзора относительно горизонта, т. Е. 0 на горизонте, +90 над головой) и «наклон» камеры (как далеко устройство от вертикали его плоскости x / y, т.е. .0 при вертикальной / книжной ориентации, +/- 90 при горизонтальной / альбомной ориентации), обе из которых получены из компонентов вектора гравитации устройства.

Vector3D eye, scene, up;
CGFloat distanceFromScene = 0.8;
// Adjust eye position for elevation (y/z)
eye.x = 0;
eye.y = distanceFromScene * -sin(elevationRadians); // eye position goes down as elevation angle goes up
eye.z = distanceFromScene * cos(elevationRadians);  // z position is maximum when elevation is zero 
// Lookat point is origin
scene = Vector3DMake(0, 0, 0); // Scene is at origin
// Camera tilt - involves x/y plane only - arbitrary vector length
up.x = sin(tiltRadians);
up.y = cos(tiltRadians);
up.z = 0;

Затем вы просто применяете преобразование gluLookAt, а также поворачиваете сцену в соответствии с заголовком устройства.

// Adjust view for device orientation
gluLookAt(eye.x, eye.y, eye.z, scene.x, scene.y, scene.z, up.x, up.y, up.z);
// Apply device heading to scene
glRotatef(currentHeadingDegrees, 0.0, 1.0, 0.0);
0 голосов
/ 30 марта 2010

оо, хорошо.

GLGravity, кажется, все делает правильно, кроме рыскания.Вот что я бы попробовал.Делайте все, что делает GLGravity, и затем это:

Проецируйте вектор в направлении, в котором вы хотите повернуть чайник, используя компас или что-то другое по вашему выбору.Затем умножьте вектор «вперед» на текущую матрицу вращения чайника, которая даст вам направление, в котором смотрит на чайник .Сведите два вектора к горизонтальной плоскости и возьмите угол между ними.

Этот угол - ваш корректирующий рывок.Тогда просто glRotatef.

Является ли компас 3GS надежным и достаточно надежным, чтобы это работало, это другое дело.Обычные компасы не работают, когда северный вектор перпендикулярен их лицу.Но я только что попробовал приложение «Карты» на 3GS моего напарника, и оно, похоже, справляется, поэтому, возможно, у них есть механическое решение.Знание того, что на самом деле делает устройство, поможет интерпретировать результаты, которые оно дает.

Обязательно протестируйте свое приложение на северном и южном полюсах, как только вы закончите.: -)

0 голосов
/ 30 марта 2010

Попробуйте повернуть объект в зависимости от значений ускорения iphone.

float angle = -atan2(accelX, accelY);

glPushMatrix();     
glTranslatef(centerPoint.x, centerPoint.y, 0);
glRotatef(angle, 0, 0, 1);
glTranslatef(-centerPoint.x, -centerPoint.y, 0);
glPopMatrix();

Где centerPoint - это средняя точка объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...