Обращайте камеру по кругу вокруг сферы, глядя в центр (используя кватернионы)

Я просмотрел массу примеров кватернионов на нескольких сайтах, включая этот, но не нашел ни одного ответа на этот вопрос, так что поехали ...

Я хочу, чтобы моя камера вращалась вокруг большой сферы, и чтобы камера всегда была направлена ​​в центр сферы. Чтобы упростить задачу, сфера расположена в точке {0,0,0} в мире. Я использую кватернион для ориентации камеры и вектор для положения камеры. Проблема в том, что положение камеры идеально вращается вокруг сферы, как и должно, но всегда смотрит в одном постоянном направлении прямо вперед, вместо того, чтобы настраиваться так, чтобы указывать на центр, когда она вращается.

Это должно быть что-то простое, но я новичок в кватернионах ... что мне не хватает?

Я использую C ++, DirectX9. Вот мой код:

// Note: g_camRotAngleYawDir and g_camRotAnglePitchDir are set to either 1.0f, -1.0f according to keypresses, otherwise equal 0.0f
// Also, g_camOrientationQuat is just an identity quat to begin with.

float theta = 0.05f;
D3DXQUATERNION g_deltaQuat( 0.0f, 0.0f, 0.0f, 1.0f );
D3DXQuaternionRotationYawPitchRoll(&g_deltaQuat, g_camRotAngleYawDir * theta, g_camRotAnglePitchDir * theta, g_camRotAngleRollDir * theta);
D3DXQUATERNION tempQuat = g_camOrientationQuat;
D3DXQuaternionMultiply(&g_camOrientationQuat, &tempQuat, &g_deltaQuat);

D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);
g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;

g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );

[РЕДАКТИРОВАТЬ - 13 февраля 2012 г.]

Хорошо, вот мое понимание:

перемещайте камеру, используя угловую дельту для каждого кадра. Получите вектор от центра к положению камеры. Вызовите quaternionRotationBetweenVectors с единичным вектором, направленным по оси z, и целевым вектором. Затем используйте результат этой функции для ориентации матрицы обзора, и положение камеры войдет в часть трансляции матрицы обзора.

Вот новый код (вызываемый каждым кадром) ...

//  This part orbits the position around the sphere according to deltas for yaw, pitch, roll
D3DXQuaternionRotationYawPitchRoll(&deltaQuat, yawDelta, pitchDelta, rollDelta);

D3DXMatrixRotationQuaternion(&mat1, &deltaQuat);

D3DXVec3Transform(&g_camPosition, &g_camPosition, &mat1);

// Эта часть регулирует ориентацию камеры так, чтобы она указывала на центр сферы

dir1 = normalize(vec3(0.0f, 0.0f, 0.0f) - g_camPosition);

QuaternionRotationBetweenVectors(&g_camOrientationQuat, vec3(0.0f, 0.0f, 1.0f), &dir1);


D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);

g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;


g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );

... Я попробовал это решение, но безуспешно. Что я делаю неправильно?


person jessejuicer    schedule 12.02.2012    source источник
comment
Знаете ли вы, что матрица обзора - это не преобразование камеры в мировом пространстве, а преобразование из мирового пространства в систему координат камеры (обратная)?   -  person rasmus    schedule 14.02.2012
comment
Попробуйте сделать D3DXMatrixInverse(&g_viewMatrix, NULL, &g_viewMatrix); перед SetTransform.   -  person kloffy    schedule 14.02.2012
comment
Это определенно устранило одну мою проблему. Спасибо!   -  person jessejuicer    schedule 15.02.2012


Ответы (2)


Это должно быть так же просто, как выполнение следующих действий всякий раз, когда вы обновляете положение (при условии, что камера направлена ​​вдоль оси z):

direction = normalize(center - position)
orientation = quaternionRotationBetweenVectors(vec3(0,0,1), direction)

Достаточно легко найти примеры того, как реализовать quaternionRotationBetweenVectors. Вы можете начать с вопроса «Поиск кватерниона, представляющего вращение от одного вектора к другому ".

Вот непроверенный набросок реализации с использованием API DirectX9:

D3DXQUATERNION* quaternionRotationBetweenVectors(__inout D3DXQUATERNION* result, __in const D3DXVECTOR3* v1, __in const D3DXVECTOR3* v2)
{
    D3DXVECTOR3 axis;

    D3DXVec3Cross(&axis, v1, v2);
    D3DXVec3Normalize(&axis, &axis);

    float angle = acos(D3DXVec3Dot(v1, v2));

    D3DXQuaternionRotationAxis(result, &axis, angle);

    return result;
}

Эта реализация ожидает, что v1, v2 будет нормализован.

person kloffy    schedule 12.02.2012
comment
хммм ... возникла проблема с размещением кода в этом поле для комментариев ... Я добавлю правку в исходное сообщение ... - person jessejuicer; 14.02.2012

Вам нужно показать, как вы рассчитываете углы. Есть ли причина, по которой вы не используете D3DXMatrixLookAtLH?

person rasmus    schedule 12.02.2012
comment
Ну, я использовал D3DXMatrixLookAtLH, но поскольку часть формулы, которую я использовал, требовала вектора вверх, камера срабатывала на северном и южном полюсах сферы. Я думаю, это известно как Gimbal Lock? Я читал, что использование кватернионов может избежать этой проблемы. На самом деле я также попытался повернуть свой вектор вверх, когда камера вращалась вокруг сферы, так как я думал, что это поможет избежать проблемы. Это было просто метание дротика в темноту, и ничего не вышло. - person jessejuicer; 14.02.2012
comment
Что касается вычисления углов, на самом деле я просто увеличиваю угол на 0,05f каждый кадр, когда клавиша удерживается нажатой. Вот подробности: клавиша a делает yawDir = 1.0f, а клавиша d делает yawDir -1.0f. Если ни одна из этих клавиш не нажата, тогда yawDir = 0.0f; Затем я вызываю UpdateCameraMatrices () в каждом кадре, который содержит код из моего первого сообщения. yawDir затем умножается на theta (0,05f), чтобы получить угол 0,05f, -0,05f или 0,0f для рыскания. То же самое я делаю с питчем. Бросок никогда не меняется с 0,0f. - person jessejuicer; 14.02.2012
comment
Проблему блокировки кардана легко избежать, вычисляя подходящий восходящий вектор для каждого кадра. - person rasmus; 14.02.2012
comment
rasmus: Пытаясь довольно долго, я должен спросить, как вычислить подходящий восходящий вектор? Я попытался повернуть свой вектор вверх, когда камера вращалась вокруг сферы, так, чтобы вектор вверх соответствовал новой ориентации камеры, то есть он всегда был прямо с точки зрения камеры. Это не сработало. Это неправильный подход? - person jessejuicer; 05.06.2012
comment
Сверху в голове: up = cross(camPos - centerOfSphere, camMatrixInverse * vec3(1,0,0)), где camMatrixInverse - из предыдущего кадра, а camPos - это новая позиция в этом кадре. Не забудьте нормализовать up. Пожалуйста, примите мой ответ, если это сработает для вас. - person rasmus; 07.06.2012