如何使用重力矢量正确转换场景以获得增强现实?

时间:2010-03-29 22:27:13

标签: iphone objective-c opengl-es

我正在尝试弄清楚如何根据设备方向正确显示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度。但是,当设备(在横向视图中)向前/向后倾斜时,它仍会在航向中产生重大跳跃。所以有些不对劲。它表明上述实际航向的计算是不正确的......

4 个答案:

答案 0 :(得分:3)

我终于找到了一个有效的解决方案。 : - )

我放弃了旋转矩阵方法,而是采用了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);

答案 1 :(得分:1)

现在可以使用CMMotionManager获得更加稳定的基于重力的参考。

使用startDeviceMotionUpdates()开始运动更新时,可以指定reference frame

这会将加速度计,陀螺仪和可选的(取决于选择的参考系)磁力计数据融合在一起。加速度计的数据非常嘈杂和有弹性(设备的任何侧向运动都会使重力矢量因设备的任何加速度而暂时倾斜),并且仅凭此无法提供很好的参考。

我一直在对加速度计数据进行低通滤波,这虽然有所帮助,但会使系统运行缓慢。

答案 2 :(得分: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是对象的中间点。

答案 3 :(得分:0)

哦,很好。

除了偏航之外,GLGravity似乎能让一切正确。这是我会尝试的。做GLGravity所做的一切,然后这个:

使用指南针或您选择的任何方式,按照您想要茶壶面对的方向投影矢量。然后将“前向”矢量乘以茶壶的当前旋转矩阵,这将为您提供茶壶 所面向的方向。将两个矢量展平到水平面并获取它们之间的角度。

这个角度是你的矫正偏航。然后只需glRotatef

3GS指南针是否可靠且足够强大,可以使其工作,这是另一回事。当北向量垂直于其面时,正常罗盘不起作用。但我刚刚在我的同事的3GS上尝试了地图应用程序,似乎应对,所以也许他们在那里有一个机械解决方案。知道设备实际在做什么将有助于解释它给出的结果。

确保在完成后在北极和南极测试您的应用。 : - )