如何使用 iPhone 加速度计的重力矢量控制 openGL 中的相机

发布于 2024-07-26 08:09:36 字数 239 浏览 5 评论 0原文

我有一个相机结构,用于保存位置、向上和方向向量。 我想根据从 iPhone 加速度计获得的重力矢量来更新这些矢量。 我想要的效果是:当手机顶部远离你倾斜时,摄像头朝向地面。 换句话说,场景/几何形状遵循重力矢量的方向,而相机则遵循手机本身的方向。

我想我可以将根据相机向量构建的矩阵乘以根据重力向量构建的矩阵,然后取出新的向上和方向向量,但我认为我没有完全理解这个过程,因为我无法得到让它工作。 我非常感谢任何帮助。 谢谢!

I have a camera structure that holds location, up, and direction vectors. I'd like to update these vectors based on the gravity vector I get from the iPhone's accelerometer. The effect I'm going for is: when the top of the phone is tilted away from you, the camera looks towards the ground. In other words, the scene/geometry follows the orientation of the gravity vector while the camera follows the orientation of the phone itself.

I thought I could multiply the matrix I built from the camera vectors by the one built from the gravity vector and then just pull out the new up and direction vectors, but I don't think I fully understand the process as I can't get it to work. I'd greatly appreciate any help. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

莫多说 2024-08-02 08:09:36

您只需要更新相机的观察方向即可。 您不必更改世界矩阵,openGL“gluLookAt()”方法会在幕后自动为您完成此操作。

如果您有相机类设置,只需创建一个函数来设置相机的向上方向向量,您需要根据 iPhone 指南针的浮点/双精度值(我假设)来计算该向量。 当你的相机更新它的lookAt()位置时,它应该改变相机以查看正确的位置。

这与在基于 FPS 的游戏中旋转相机时所做的事情没有太大不同。 不同之处在于您希望沿X 轴而不是沿Y 轴旋转相机。

看一下相机类如何执行旋转以使用键盘向左或向右移动相机,然后修改它以使用指南针方向值进行工作。

下面是我编写的一些 C++ 代码,它可以让您深入了解相机类应该如何工作:

/* This reshapes the camera using the perspective projection */
void Camera::ReshapePerspectiveForPicking( void )
{   
    glMatrixMode(GL_PROJECTION);

    // Sets the clipping volume
    gluPerspective( m_FieldOfView, (float)m_width/(float)m_height, m_zNear, m_zFar );

    gluLookAt(  camPos.x, camPos.y, camPos.z, 
            camPos.x + camView.x,   camPos.y + camView.y,   camPos.z + camView.z,
            0.0f, 1.0f, 0.0f );

    glMatrixMode( GL_MODELVIEW );
}

记下上面的行 (0.0f, 1.0f, 0.0f)。 这是向上方向向量。
对于我的游戏来说它是静态的,因为摄像机永远不需要向下看。
您只需要通过在罗盘方向上创建一个新的向上矢量来更改此矢量即可。

下面的方法只是我们有时需要通过传递特殊向量来更新相机的替代方法。 您可能可以忽略它,我只是将其包含在内,以便您可以从中学习。

   /* This updates the camera to look at the changed camera position. This uses a passed in camPosition and camView GameMath::Vector */
    void Camera::Update( GameMath::Vector camPos, GameMath::Vector camView )
    {
        glMatrixMode( GL_PROJECTION );
        gluLookAt(  camPos.x, camPos.y, camPos.z, 
                camPos.x + camView.x,   camPos.y + camView.y,   camPos.z + camView.z,
                0.0f, 1.0f,0.0f );
    }

这是我沿 Y 轴旋转相机的方法(记住你想沿 X 轴旋转)——我现在会重写这个方法,因为它有点狡猾(我几年前写的) )但这足以向您展示如何做到这一点。

void Camera::Rotate( void )
{
    if ( m_rotateCamera == true )
    {
        // Keep the radians within 2 pi, roughly
        float minimumRadiansRotate = 0.00;
        float maximumRadiansRotate = 6.2831853072;
        m_YRotateAngle = GameMath::Wrap( m_YRotateAngle, minimumRadiansRotate, maximumRadiansRotate );

        m_YRotateAngle += m_rotateSpeed * m_rotateDirection;    // Add to the camera's current angle value
        camView.x = sin( m_YRotateAngle );
        camView.z = -cos( m_YRotateAngle );
    }
}

为您提供一段特定的代码来完成您想做的事情有点困难,因为您的相机类可能与我的不同,尽管这应该让您了解需要做什么。

CoreLocation 框架包含从指南针读取值所需的代码位(如果您尚未对该部分进行编码)。

祝你好运。

You would simply need to update the direction in which your camera is looking. You don't have to change the world matrix, the openGL "gluLookAt()" method does that for you automatically behind the scenes.

If you have a camera class setup, simply create a function to set the camera's up direction vector which you will need to obtain calculate based on a float/double value (i assume) from the iPhone's compass. When your camera update's it's lookAt() position it should change the camera to look in the correct location.

This is not that much different to what you do when you rotate your camera in an FPS based game. The difference is that you want to rotate the camera along the X-Axis instead of along the Y-Axis.

Take a look at how a camera class performs rotation for moving the camera left or right with the keyboard, then modify it to work using your compass direction values.

Here is some C++ code I wrote which may give you an insight as to how your camera class should be working:

/* This reshapes the camera using the perspective projection */
void Camera::ReshapePerspectiveForPicking( void )
{   
    glMatrixMode(GL_PROJECTION);

    // Sets the clipping volume
    gluPerspective( m_FieldOfView, (float)m_width/(float)m_height, m_zNear, m_zFar );

    gluLookAt(  camPos.x, camPos.y, camPos.z, 
            camPos.x + camView.x,   camPos.y + camView.y,   camPos.z + camView.z,
            0.0f, 1.0f, 0.0f );

    glMatrixMode( GL_MODELVIEW );
}

Take note to the line above (0.0f, 1.0f, 0.0f). This is the UP direction vector.
It was static for my game because the camera never needed to look down.
You would simply need to change this vector by created a new up vector on the compass orientation.

The method below was simply an alternative method we needed to sometimes update the camera by passing it a special vector. You can probably ignore it, I just included it so you could learn from it.

   /* This updates the camera to look at the changed camera position. This uses a passed in camPosition and camView GameMath::Vector */
    void Camera::Update( GameMath::Vector camPos, GameMath::Vector camView )
    {
        glMatrixMode( GL_PROJECTION );
        gluLookAt(  camPos.x, camPos.y, camPos.z, 
                camPos.x + camView.x,   camPos.y + camView.y,   camPos.z + camView.z,
                0.0f, 1.0f,0.0f );
    }

Here is my method for rotating the camera along the Y-Axis (Remember you want to rotate along the X-Axis) --I would rewrite this method now because it's kinda dodgey (I wrote it years ago) but it's enough to show you how it can be done.

void Camera::Rotate( void )
{
    if ( m_rotateCamera == true )
    {
        // Keep the radians within 2 pi, roughly
        float minimumRadiansRotate = 0.00;
        float maximumRadiansRotate = 6.2831853072;
        m_YRotateAngle = GameMath::Wrap( m_YRotateAngle, minimumRadiansRotate, maximumRadiansRotate );

        m_YRotateAngle += m_rotateSpeed * m_rotateDirection;    // Add to the camera's current angle value
        camView.x = sin( m_YRotateAngle );
        camView.z = -cos( m_YRotateAngle );
    }
}

It's a bit difficult to provide you with a specific piece of code to do what you want to do because your camera class is probably different to mine, although this should get you to understand what needs to be done.

The CoreLocation framework contains the bits of code you will need to read values from the compass incase you haven't coded that part as yet.

Good luck.

只怪假的太真实 2024-08-02 08:09:36

我认为 Apple 的 示例 GLGravity 应用程序 正是您想要的,除非我读错了你的请求。

I think that Apple's sample GLGravity application does exactly what you want, unless I'm reading your request wrong.

淤浪 2024-08-02 08:09:36

我首先要说的是,我对这个东西很陌生,所以要小心,但是......

我看了上面布洛克的代码和他的解释,我想我想出了一个围绕x轴和y轴旋转的相机。 实际上,我的相机也围绕 z 旋转,但我认为这是一个不同的故事(我只是将 x 和 y 加速度计值中的过滤值直接提供给“向上”向量,以创建我的对象受到真实影响的错觉重力...效果很好)。

所以...这就是我想到的:

float lx = sin(DEGREES_TO_RADIANS(horzAngle));
float ly = sin(DEGREES_TO_RADIANS(vertAngle));
float lz = -cos(DEGREES_TO_RADIANS(horzAngle));

float x;
float y;
float z;

    // get the default camera eye for the object
    // this will center the object on the screen
[sprite.camera restore];
[sprite.camera eyeX:&x eyeY:&y eyeZ:&z];

// add distance calcs
x = x + (-1 * sprite.distance)*(lx);
z = z + (-1 * sprite.distance)*(-1);

[sprite.camera setEyeX:x eyeY:y eyeZ:z];
[sprite.camera setCenterX:x + lx centerY:y + ly centerZ:z + lz];

这是使用 cocos2d 库(非常容易使用)...它包装了相机的内容,以便它最终会像这样调用 gluLookAt

    gluLookAt( eyeX, eyeY, eyeZ,
            centerX, centerY, centerZ,
            upX, upY, upZ
            );

就像我说的,我对此很陌生,所以这可能看起来不太正确,但似乎...我打算添加加速度计来控制 vertAngle,正如您所描述的那样,所以当我连接加速代码时,我会尽力记住将其发布在这里。

另外,如果我遗漏了其他人可以添加一些亮点的东西,我很想听听。

谢谢,

约翰

i gotta start by sayin, i'm very new to this stuff, so be wary, but...

i looked at brock's code above and his explanation and i think i came up with a camera rotating around both the x and the y axis. actually, my camera rotates around the z too, but i think that is a different story (i just feed filtered values from the x & y accelerometer values directly to the "up" vector to create the illusion that my objects are affected by real gravity... works pretty good).

so... here is what i came up with:

float lx = sin(DEGREES_TO_RADIANS(horzAngle));
float ly = sin(DEGREES_TO_RADIANS(vertAngle));
float lz = -cos(DEGREES_TO_RADIANS(horzAngle));

float x;
float y;
float z;

    // get the default camera eye for the object
    // this will center the object on the screen
[sprite.camera restore];
[sprite.camera eyeX:&x eyeY:&y eyeZ:&z];

// add distance calcs
x = x + (-1 * sprite.distance)*(lx);
z = z + (-1 * sprite.distance)*(-1);

[sprite.camera setEyeX:x eyeY:y eyeZ:z];
[sprite.camera setCenterX:x + lx centerY:y + ly centerZ:z + lz];

this is using cocos2d library (very easy to use)... this wraps the camera stuff so that it'll end up calling gluLookAt like this:

    gluLookAt( eyeX, eyeY, eyeZ,
            centerX, centerY, centerZ,
            upX, upY, upZ
            );

like i said, i'm new to this, so this might not look right, but it seems to... i intend to add the accelerometer to control the vertAngle as you are describing, so when i get the accel code wired up, i'll try and remember to post that here.

also, if i'm missing something that someone else can add some light to, i'd love to hear it.

thanks,

john

我偏爱纯白色 2024-08-02 08:09:36

您真正想要的是基于加速度值的新投影矩阵。 Oolong 提供了这个疯狂数学< /a> 来做到这一点(不是我的)。

/*
Oolong Engine for the iPhone / iPod touch
Copyright (c) 2007-2008 Wolfgang Engel  http://code.google.com/p/oolongengine/

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it freely, 
subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#import "Accelerometer.h"

#define FILTERINGFACTOR 0.1

@implementation Accel

- (void) SetupAccelerometer: (float) AcclerometerFrequency
{
        //Configure and start accelerometer
        [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / AcclerometerFrequency)];
        [[UIAccelerometer sharedAccelerometer] setDelegate:self];
}



- (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)Acceleration
{
        // use a basic low-pass filter to only keep the gravity in the accelerometer values
        _accelerometer[0] = Acceleration.x * FILTERINGFACTOR + _accelerometer[0] * (1.0 - FILTERINGFACTOR);
        _accelerometer[1] = Acceleration.y * FILTERINGFACTOR + _accelerometer[1] * (1.0 - FILTERINGFACTOR);
        _accelerometer[2] = Acceleration.z * FILTERINGFACTOR + _accelerometer[2] * (1.0 - FILTERINGFACTOR);
}

- (void) GetAccelerometerMatrix:(GLfloat *) matrix
{

        GLfloat length = sqrtf(_accelerometer[0] * _accelerometer[0] + _accelerometer[1] * _accelerometer[1] + _accelerometer[2] * _accelerometer[2]);

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

        //Setup first matrix column as gravity vector
        matrix[0] = _accelerometer[0] / length;
        matrix[1] = _accelerometer[1] / length;
        matrix[2] = _accelerometer[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[4] = 0.0;
        matrix[5] = 1.0;
        matrix[6] = -_accelerometer[1] / _accelerometer[2];
        length = sqrtf(matrix[4] * matrix[4] + matrix[5] * matrix[5] + matrix[6] * matrix[6]);
        matrix[4] /= length;
        matrix[5] /= length;
        matrix[6] /= length;

        //Setup third matrix column as the cross product of the first two
        matrix[8] = matrix[1] * matrix[6] - matrix[2] * matrix[5];
        matrix[9] = matrix[4] * matrix[2] - matrix[6] * matrix[0];
        matrix[10] = matrix[0] * matrix[5] - matrix[1] * matrix[4];
}

- (void) GetAccelerometerVector:(double *) AccelValue;
{
        // the vector is read-only, so make a copy of it and do not expose a pointer to it
        AccelValue[0] = (double)_accelerometer[0];
        AccelValue[1] = (double)_accelerometer[1];
        AccelValue[2] = (double)_accelerometer[2];
}

@end

What you really want is a new projection matrix based on acceleration values. Oolong provides this crazy math to do that (not mine).

/*
Oolong Engine for the iPhone / iPod touch
Copyright (c) 2007-2008 Wolfgang Engel  http://code.google.com/p/oolongengine/

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it freely, 
subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#import "Accelerometer.h"

#define FILTERINGFACTOR 0.1

@implementation Accel

- (void) SetupAccelerometer: (float) AcclerometerFrequency
{
        //Configure and start accelerometer
        [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / AcclerometerFrequency)];
        [[UIAccelerometer sharedAccelerometer] setDelegate:self];
}



- (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)Acceleration
{
        // use a basic low-pass filter to only keep the gravity in the accelerometer values
        _accelerometer[0] = Acceleration.x * FILTERINGFACTOR + _accelerometer[0] * (1.0 - FILTERINGFACTOR);
        _accelerometer[1] = Acceleration.y * FILTERINGFACTOR + _accelerometer[1] * (1.0 - FILTERINGFACTOR);
        _accelerometer[2] = Acceleration.z * FILTERINGFACTOR + _accelerometer[2] * (1.0 - FILTERINGFACTOR);
}

- (void) GetAccelerometerMatrix:(GLfloat *) matrix
{

        GLfloat length = sqrtf(_accelerometer[0] * _accelerometer[0] + _accelerometer[1] * _accelerometer[1] + _accelerometer[2] * _accelerometer[2]);

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

        //Setup first matrix column as gravity vector
        matrix[0] = _accelerometer[0] / length;
        matrix[1] = _accelerometer[1] / length;
        matrix[2] = _accelerometer[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[4] = 0.0;
        matrix[5] = 1.0;
        matrix[6] = -_accelerometer[1] / _accelerometer[2];
        length = sqrtf(matrix[4] * matrix[4] + matrix[5] * matrix[5] + matrix[6] * matrix[6]);
        matrix[4] /= length;
        matrix[5] /= length;
        matrix[6] /= length;

        //Setup third matrix column as the cross product of the first two
        matrix[8] = matrix[1] * matrix[6] - matrix[2] * matrix[5];
        matrix[9] = matrix[4] * matrix[2] - matrix[6] * matrix[0];
        matrix[10] = matrix[0] * matrix[5] - matrix[1] * matrix[4];
}

- (void) GetAccelerometerVector:(double *) AccelValue;
{
        // the vector is read-only, so make a copy of it and do not expose a pointer to it
        AccelValue[0] = (double)_accelerometer[0];
        AccelValue[1] = (double)_accelerometer[1];
        AccelValue[2] = (double)_accelerometer[2];
}

@end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文