Элементы управления OpenGL Matrix Camera, локальное вращение не работает должным образом

Поэтому я пытаюсь выяснить, как вручную создать класс камеры, который создает локальный кадр для преобразований камеры. Я создал объект игрока на основе класса GLFrame OpenGL SuperBible.

Я получил клавиши клавиатуры, связанные с функциями MoveUp, MoveRight и MoveForward, а горизонтальные и вертикальные движения мыши были сопоставлены с переменной xRot и функцией rotateLocalY. Это сделано для создания камеры в стиле FPS.

Проблема, однако, в RotateLocalY. Перевод работает хорошо, как и вертикальное движение мыши, но горизонтальное перемещение странным образом масштабирует все мои объекты вниз или вверх. Помимо масштабирования, вращение также, по-видимому, ограничивается 180 градусами и вращается вокруг начала координат мира (0.0) вместо локальной позиции моего игрока.

Я полагал, что масштабирование как-то связано с нормализацией векторов, но класс GLframe (который я использовал для справки) никогда не нормализует какие-либо векторы, и этот класс работает просто отлично. Нормализация большинства моих векторов только решила масштабирование, и все остальные проблемы были все еще там, так что я полагаю, что один кусок кода вызывает все эти проблемы?

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

Объект игрока

Player::Player()
{
location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f;
up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f;
}

// Does all the camera transformation. Should be called before scene rendering!
void Player::ApplyTransform()
{
M3DMatrix44f cameraMatrix;
this->getTransformationMatrix(cameraMatrix);

glRotatef(xAngle, 1.0f, 0.0f, 0.0f);
glMultMatrixf(cameraMatrix);
}

void Player::MoveForward(GLfloat delta)
{
location[0] += forward[0] * delta;
location[1] += forward[1] * delta;
location[2] += forward[2] * delta;
}

void Player::MoveUp(GLfloat delta)
{
location[0] += up[0] * delta;
location[1] += up[1] * delta;
location[2] += up[2] * delta;
}

void Player::MoveRight(GLfloat delta)
{
// Get X axis vector first via cross product
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, forward);

location[0] += xAxis[0] * delta;
location[1] += xAxis[1] * delta;
location[2] += xAxis[2] * delta;
}

void Player::RotateLocalY(GLfloat angle)
{
// Calculate a rotation matrix first
M3DMatrix44f rotationMatrix;
// Rotate around the up vector
m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used.

// Get new forward vector out of the rotation matrix
M3DVector3f newForward;
newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2];
newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2];
newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2];

m3dCopyVector3(forward, newForward);
}

void Player::getTransformationMatrix(M3DMatrix44f matrix)
{
// Get Z axis (Z axis is reversed with camera transformations)
M3DVector3f zAxis;
zAxis[0] = -forward[0];
zAxis[1] = -forward[1];
zAxis[2] = -forward[2];

// Get X axis
M3DVector3f xAxis;
m3dCrossProduct(xAxis, up, zAxis);

// Fill in X column in transformation matrix
m3dSetMatrixColumn44(matrix, xAxis, 0); // first column
matrix[3] = 0.0f; // Set 4th value to 0

// Fill in the Y column
m3dSetMatrixColumn44(matrix, up, 1); // 2nd column
matrix[7] = 0.0f;

// Fill in the Z column
m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column
matrix[11] = 0.0f;

// Do the translation
M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis)
negativeLocation[0] = -location[0];
negativeLocation[1] = -location[1];
negativeLocation[2] = -location[2];
m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column
matrix[15] = 1.0f;
}

Заголовок объекта Player

class Player
{
public:
//////////////////////////////////////
// Variables
M3DVector3f location;
M3DVector3f up;
M3DVector3f forward;
GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS)
/////////////////////////////////////
// Functions
Player();
void ApplyTransform();
void MoveForward(GLfloat delta);
void MoveUp(GLfloat delta);
void MoveRight(GLfloat delta);
void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform)

private:
void getTransformationMatrix(M3DMatrix44f matrix);
};

Преобразования пошли не так

Применение преобразований

// Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Apply camera transforms
player.ApplyTransform();// Set up lights
...

// Use shaders
...

// Render the scene
RenderScene();

// Do post rendering operations
glutSwapBuffers();

и мышь

float mouseSensitivity = 500.0f;

float horizontal = (width / 2) - mouseX;
float vertical = (height / 2) - mouseY;

horizontal /= mouseSensitivity;
vertical /= (mouseSensitivity / 25);

player.xAngle += -vertical;
player.RotateLocalY(horizontal);

glutWarpPointer((width / 2), (height / 2));

1

Решение

Честно говоря, я думаю, что вы идете к сложному подходу к вашей проблеме. Есть много способов создать камеру. Мой фаворит — использование R3-Vector и Quaternion, но вы также можете работать с R3-Vector и двумя поплавками (тангаж и рыскание).

Установка с двумя углами проста:

glLoadIdentity();
glTranslatef(-pos[0], -pos[1], -pos[2]);
glRotatef(-yaw, 0.0f, 0.0f, 1.0f);
glRotatef(-pitch, 0.0f, 1.0f, 0.0f);

Самая сложная часть — это перемещение камеры. Вы должны сделать что-то вроде:

flaot ds = speed * dt;
position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0)));

Как сделать преобразования, я должен был бы посмотреть это, но вы могли бы это с помощью матрица вращения

Вращение тривиально, просто добавьте или вычтите значения высоты и рыскания.

Мне нравится использовать кватернион для ориентации, потому что он общий, и поэтому у вас есть камера (любая сущность), которая не зависит от схемы движения. В этом случае у вас есть камера, которая выглядит так:

class Camera
{
public:
// lots of stuff omitted

void setup();

void move_local(Vector3f value);

void rotate(float dy, float dz);

private:
mx::Vector3f position;
mx::Quaternionf orientation;
};

Тогда код установки использует бесстыдно gluLookAt; Вы могли бы сделать из нее матрицу преобразования, но я так и не смог заставить ее работать правильно.

void Camera::setup()
{
// projection related stuff

mx::Vector3f eye     = position;
mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0));
mx::Vector3f center  = eye + forward;
mx::Vector3f up      = mx::transform(orientation, mx::Vector3f(0, 0, 1));
gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2));
}

Перемещение камеры в локальном кадре также просто:

void Camera::move_local(Vector3f value)
{
position += mx::transform(orientation, value);
}

Вращение тоже прямое.

void Camera::rotate(float dy, float dz)
{
mx::Quaternionf o = orientation;
o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o;
o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0));
orientation = o;
}

(Бесстыдная заглушка):

Если вы спросите, какую математическую библиотеку я использую, это mathex. Я это написал…

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]