【问题标题】:How do I use the gravity vector to correctly transform scene for augmented reality?如何使用重力矢量正确转换场景以实现增强现实?
【发布时间】:2010-03-29 22:27:13
【问题描述】:

我正在尝试弄清楚如何根据设备方向(即根据加速度计的重力矢量和指南针的方向)正确显示 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;

跟进 Chris 的建议:由于行/列顺序和标题 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 度。但是,当设备(在横向视图中)向前/向后倾斜时,它仍然会在航向上发生重大跳跃。所以有些不对劲。提示上述实际航向计算不正确...

【问题讨论】:

    标签: iphone objective-c opengl-es


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      尝试根据 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是对象的中点。

      【讨论】:

      • 勇敢的尝试,但我认为这不太对。您正在计算的角度是设备关于 z 轴(即垂直于其屏幕的轴)的倾斜量,这不是这里需要的。
      【解决方案3】:

      哦,不错。

      GLGravity 似乎把所有东西都做对了,除了偏航。这是我会尝试的。做 GLGravity 所做的一切,然后:

      使用指南针或您选择的任何东西,在您希望茶壶面对的方向上投影一个矢量。然后将“前向”向量乘以茶壶的当前旋转矩阵,这将为您提供茶壶面对的方向。将两个向量展平到水平面并取它们之间的角度。

      这个角度是你的修正偏航角。然后只需 glRotatef 就可以了。

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

      完成后,请务必在北极和南极测试您的应用。 :-)

      【讨论】:

        【解决方案4】:

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

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

        这融合了加速度计、陀螺仪和可选的(取决于选择的参考框架)磁力计数据。加速度计数据非常嘈杂且有弹性(设备的任何横向运动都会通过任何设备加速度暂时倾斜重力矢量),单独并不能作为很好的参考。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-27
          • 2011-07-21
          • 2011-11-12
          • 1970-01-01
          相关资源
          最近更新 更多