Как управлять камерой в openGL с помощью вектора гравитации с акселерометра iPhone

У меня есть структура камеры, которая содержит векторы местоположения, вверх и направления. Я хотел бы обновить эти векторы на основе вектора силы тяжести, который я получаю от акселерометра iPhone. Эффект, которого я добиваюсь, таков: когда верхняя часть телефона наклонена от вас, камера смотрит в землю. Другими словами, сцена/геометрия следует ориентации вектора гравитации, в то время как камера следует ориентации самого телефона.

Я думал, что смогу умножить матрицу, которую я построил из векторов камеры, на матрицу, построенную из вектора гравитации, а затем просто вытащить новые векторы вверх и направления, но я не думаю, что полностью понимаю процесс, поскольку я не могу понять это работать. Буду очень признателен за любую помощь. Спасибо!


person Community    schedule 13.07.2009    source источник


Ответы (4)


Вам просто нужно обновить направление, в котором смотрит ваша камера. Вам не нужно менять мировую матрицу, метод openGL «gluLookAt()» сделает это за вас автоматически за кулисами.

Если у вас есть настройка класса камеры, просто создайте функцию для установки вектора направления камеры вверх, который вам нужно будет вычислить на основе значения float/double (я полагаю) из компаса 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 содержит биты кода, которые вам понадобятся для чтения значений из компаса, если вы еще не закодировали эту часть.

Удачи.

person Brock Woolf    schedule 13.07.2009
comment
Это будет работать для более нового iPhone со встроенным компасом (если я неправильно понимаю, что вы объясняете), но я работаю только с акселерометром, поэтому все, что у меня есть в любой момент, это новый вектор вверх. Кроме того, не гарантируется, что новый вектор вверх будет перпендикулярен оси x камеры, так как же мне определить новое направление камеры? - person ; 14.07.2009

Я думаю, что пример приложения GLGravity от Apple делает именно то, что вам нужно. , если я не читаю ваш запрос неправильно.

person Brad Larson    schedule 13.07.2009
comment
Да, это очень похоже на то, чего я пытаюсь достичь. Вот почему я начал свое исследование с него. Однако получение этого эффекта при сохранении векторов ориентации для последующего использования - это то, где я нахожу трудность. Спасибо! - person ; 14.07.2009

я должен начать с того, что я очень новичок в этом деле, так что будьте осторожны, но...

я посмотрел на код Брока выше и его объяснение, и я думаю, что придумал камеру, вращающуюся вокруг осей 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, как вы описываете, поэтому, когда я получаю код ускорения, я постараюсь не забыть опубликовать это здесь.

Кроме того, если я упустил что-то, к чему кто-то другой может добавить немного света, я бы хотел это услышать.

Благодарность,

Джон

person john ellis    schedule 30.07.2009

Что вам действительно нужно, так это новая матрица проекций, основанная на значениях ускорения. Oolong предоставляет эту безумную математику сделать это (не мое).

/*
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
person slf    schedule 18.09.2009