假设您指的是根据鼠标移动旋转的相机:
一种实现方法是跟踪摄像机的位置及其在空间中的旋转。由于可以直接表示角度,因此球面坐标恰好方便。
float m_theta;
float m_phi;
float m_radius;
float3 m_target;
摄像机位于由m_theta,m_phi和m_radius定义的P处。通过更改这三个值,我们可以随意旋转和移动。但是,我们总是查看并旋转m_target。m_target是球体的本地原点。但是,我们可以将这个原点自由移动到世界空间中的任何地方。
相机具有三种主要功能:
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 。
您可能会问的一个问题是:为什么总是在笛卡尔和球面之间进行转换(您还必须进行转换才能创建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相机系统。该代码是免费和开源的,因此可以在您自己的项目中随意使用它。