สมมติว่าคุณหมายถึงกล้องที่หมุนตามการเคลื่อนไหวของเมาส์:
วิธีหนึ่งในการนำมันไปใช้คือการติดตามตำแหน่งกล้องและการหมุนในอวกาศ พิกัดทรงกลมมีความสะดวกสำหรับสิ่งนี้เนื่องจากคุณสามารถแสดงมุมได้โดยตรง
float m_theta;
float m_phi;
float m_radius;
float3 m_target;
กล้องตั้งอยู่ที่Pซึ่งกำหนดโดย m_theta, m_phi และ m_radius เราสามารถหมุนและเคลื่อนย้ายได้อย่างอิสระทุกที่ที่เราต้องการโดยการเปลี่ยนค่าทั้งสาม อย่างไรก็ตามเรามักจะดูและหมุนไปรอบ ๆ 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 พร้อมเวกเตอร์ขึ้นและลงทางขวา
คำถามหนึ่งที่คุณอาจถามคือ: ทำไมแปลงระหว่างคาร์ทีเซียนและทรงกลมตลอดเวลา (คุณจะต้องแปลงเพื่อสร้างมุมมองเมทริกซ์ด้วย)
คำถามที่ดี. ฉันมีคำถามนี้และพยายามใช้คาร์ทีเซียนโดยเฉพาะ คุณจบลงด้วยปัญหาการหมุน เนื่องจากการทำงานของจุดลอยตัวนั้นไม่แม่นยำอย่างแม่นยำการหมุนหลายครั้งจึงทำให้เกิดข้อผิดพลาดในการสะสมซึ่งตรงกับกล้องช้าและหมุนโดยไม่ตั้งใจ
ในที่สุดฉันก็ติดอยู่กับพิกัดทรงกลม เพื่อชดเชยการคำนวณพิเศษฉันลงเอยเมทริกซ์มุมมองแคชและคำนวณเฉพาะเมื่อกล้องเคลื่อนที่เท่านั้น
ขั้นตอนสุดท้ายคือการใช้คลาสกล้องนี้ เพียงเรียกใช้ฟังก์ชันสมาชิกที่เหมาะสมภายในฟังก์ชั่น 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 รหัสนี้ฟรีและโอเพ่นซอร์สดังนั้นอย่าลังเลที่จะใช้มันในโครงการของคุณเอง