Как использовать вектор гравитации, чтобы правильно преобразовать сцену для дополненной реальности?

StackOverflow https://stackoverflow.com/questions/2541668

  •  23-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь выяснить, как правильно отображать указанный объект 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;

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

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 градусов, когда экран устройства (в книжной ориентации) наклонен вперед/назад в вертикальном положении.Тем не менее, он по-прежнему делает значительные скачки курса, когда устройство (в альбомной ориентации) наклонено вперед/назад.Значит что-то не так.Это говорит о том, что приведенный выше расчет фактического курса неверен...

Нет правильного решения

Другие советы

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

Я отказался от подхода с матрицей вращения и вместо этого принял 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);

Попробуйте повернуть объект в зависимости от значений ускорения 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 — это средняя точка объекта.

оо, приятно.

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

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

Этот угол и есть ваше корректирующее отклонение.Тогда просто glRotatef этим.

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

После завершения обязательно протестируйте свое приложение на северном и южном полюсах.:-)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top