マウスの動きに基づいて回転するカメラを意味すると仮定します。
これを実装する1つの方法は、カメラの位置と空間内での回転を追跡することです。角度を直接表現できるため、球座標が便利です。
float m_theta;
float m_phi;
float m_radius;
float3 m_target;
カメラは、m_theta、m_phi、m_radiusで定義されるPにあります。これらの3つの値を変更することで、好きな場所を自由に回転および移動できます。ただし、m_targetを常に見て、回転します。m_targetは球のローカル原点です。ただし、ワールド空間のどこにでもこの原点を自由に移動できます。
3つの主要なカメラ機能があります。
void Rotate(float dTheta, float dPhi);
void Zoom(float distance);
void Pan(float dx, float dy);
最も単純な形式では、Rotate()とZoom()は簡単です。それぞれ、m_theta、m_phi、m_radiusを変更します。
void Camera::Rotate(float dTheta, float dPhi) {
m_theta += dTheta;
m_phi += dPhi;
}
void Camera::Zoom(float distance) {
m_radius -= distance;
}
パンはもう少し複雑です。カメラパンとは、カメラを現在のカメラビューに対してそれぞれ左右/上下に移動することです。これを実現する最も簡単な方法は、現在のカメラビューを球面座標からデカルト座標に変換することです。これにより、正しいベクトルが得られます。
void Camera::Pan(float dx, float dy) {
float3 look = normalize(ToCartesian());
float3 worldUp = float3(0.0f, 1.0f, 0.0f, 0.0f);
float3 right = cross(look, worldUp);
float3 up = cross(look, right);
m_target = m_target + (right * dx) + (up * dy);
}
inline float3 ToCartesian() {
float x = m_radius * sinf(m_phi) * sinf(m_theta);
float y = m_radius * cosf(m_phi);
float z = m_radius * sinf(m_phi) * cosf(m_theta);
float w = 1.0f;
return float3(x, y, z, w);
}
したがって、まず、球面座標系をデカルトに変換して、ルックベクトルを取得します。次に、正しいベクトルを得るために、ワールドアップベクトルとベクトル外積を行います。これは、カメラビューのすぐ右を指すベクトルです。最後に、別のベクトル外積を行って、カメラのアップベクトルを取得します。
パンを終了するには、我々は一緒にm_targetを移動アップし、右のベクトル。
質問の1つは、なぜデカルト座標と球面座標を常に変換するのかということです(Viewマトリックスを作成するために変換する必要もあります)。
良い質問。私もこの質問をして、デカルトを排他的に使用しようとしました。回転に問題が生じます。浮動小数点演算は正確に正確ではないため、複数の回転により累積エラーが発生します。これは、カメラに対応し、意図せずにローリングします。
だから、結局、私は球座標に固執しました。追加の計算に対抗するために、ビューマトリックスをキャッシュし、カメラが移動したときにのみ計算するようにしました。
最後のステップは、このCameraクラスを使用することです。アプリのMouseDown / Up / Scroll関数内で適切なメンバー関数を呼び出すだけです。
void MouseDown(WPARAM buttonState, int x, int y) {
m_mouseLastPos.x = x;
m_mouseLastPos.y = y;
SetCapture(m_hwnd);
}
void MouseUp(WPARAM buttonState, int x, int y) {
ReleaseCapture();
}
void MouseMove(WPARAM buttonState, int x, int y) {
if ((buttonState & MK_LBUTTON) != 0) {
if (GetKeyState(VK_MENU) & 0x8000) {
// Calculate the new phi and theta based on mouse position relative to where the user clicked
float dPhi = ((float)(m_mouseLastPos.y - y) / 300);
float dTheta = ((float)(m_mouseLastPos.x - x) / 300);
m_camera.Rotate(-dTheta, dPhi);
}
} else if ((buttonState & MK_MBUTTON) != 0) {
if (GetKeyState(VK_MENU) & 0x8000) {
float dx = ((float)(m_mouseLastPos.x - x));
float dy = ((float)(m_mouseLastPos.y - y));
m_camera.Pan(-dx * m_cameraPanFactor, dy * m_cameraPanFactor);
}
}
m_mouseLastPos.x = x;
m_mouseLastPos.y = y;
}
void MouseWheel(int zDelta) {
// Make each wheel dedent correspond to a size based on the scene
m_camera.Zoom((float)zDelta * m_cameraScrollFactor);
}
m_camera * Factor変数は、カメラが回転/パン/スクロールする速度を変更する単なるスケールファクターです
上記のコードは、サイドプロジェクト用に作成したカメラシステムの単純化された擬似コードバージョンです。camera.hおよびcamera.cppです。カメラはMayaカメラシステムを模倣しようとします。コードは無料でオープンソースですので、自分のプロジェクトで自由に使用してください。