一、隱藏并捕捉光標
偏航角和俯仰角是通過鼠標移動獲得的,水平的移動影響偏航角,豎直的移動影響俯仰角。
原理是,存儲上一幀鼠標的位置,在當前幀中計算鼠標位置與上一幀的位置相差多少。如果水平/豎直差別越大,那么俯仰角或偏航角就改變越大,也就是攝像機需要移動更多的距離。
首先我們應該告訴GLFW,它應該隱藏光標,并捕捉它。
結果:無論我們怎樣移動鼠標,光標都不會顯示,但它也不會離開窗口。
glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_DISABLED);
二:監聽鼠標移動事件
為了計算俯仰角和偏航角,我們需要讓GLFW監聽鼠標移動事件(和鍵盤輸入相似)。用一個回調函數來完成,函數原型如下:
void mouse_callback(GLFWwindow *window,double xpos,double ypos);
xpos,ypos代表當前鼠標的位置。當我們用GLFW注冊了回調函數之后,鼠標一移動mouse_callback函數就會被調用:
1 glfwSetCursorCallback(window,mouse_callback);
三、在處理FPS風格攝像機的鼠標輸入的時候,我們必須在最終獲取方向向量之前做下面這幾步:
1、計算鼠標距上一幀的偏移量
2、把偏移量添加到攝像機的俯仰角和偏航角中
3、對俯仰角和偏航角進行最大和最小值的限制
4、計算方向向量
第一步:計算鼠標距上一幀的偏移量,所以必須在程序中存儲上一幀的鼠標位置,我們把它的初始值設置為屏幕的中心
1 float lastX = screenWidth/2.0f; 2 float lastY = screenHeight/2.0f;
第二步:在鼠標的回調函數中計算當前幀和上一幀鼠標位置的偏移量:
1 float xoffest = xpos-lastX; 2 float yoffset = lastY - ypos;//注意這里是相反的,因為y坐標是從底部往頂部依次增大的 3 lastX = xpos; 4 lastY = ypos; 5 6 float sensitivity = 0.05f; 7 xoffest *= sensitivity; 8 yoffest *= sensitivity;
注意我們把偏移量乘以了sensitivity(靈敏度)值。如果我們忽略這個值,鼠標移動就會太大了
俯仰角(Pitch)、偏航角(Yaw)
接下來我們把偏移量加到全局變量pitch和yaw上
1 yaw +=xoffset; 2 pitch +=yoffset;
第三步:給攝像機添加一些限制,這樣攝像機就不會發生奇怪的移動了。對于俯仰角,要讓用戶不能看高于89度的地方(在90度是視角會發生逆轉,所以我們把89度看成極限),同樣也不允許-89度。
1 if(pitch>89.0f) 2 pitch = 89.0f; 3 if(pitch <-89.0f) 4 pitch = -89.0f;
注意到我們沒有給偏航角設置限制,這是因為我們不希望限制用戶的水平旋轉。
第四步:通過俯仰角和偏航角來計算得到真正的方向向量:
1 glm::vec3 front; 2 front.x = cos(glm::radians(pith))*cos(glm::radians(yaw)); 3 front.y = sin(glm::radians(pitch)); 4 front.z = cos(glm::radians(pith))*cos(glm::radians(yaw)); 5 cameraFront = glm::normolize(front);
計算出來的方向向量就會包含根據鼠標移動計算出來所有的旋轉了。
我們可以簡單的使用一個bool
變量檢驗我們是否是第一次獲取鼠標輸入,如果是,那么我們先把鼠標的初始位置更新為xpos和ypos值,這樣就能解決這個問題;接下來的鼠標移動就會使用剛進入的鼠標位置坐標來計算偏移量了:
1 if(firstMouse)//這個bool變量初始時是設定為true的 2 { 3 lastX = xpos; 4 lastY = ypos; 5 firstMouse = false; 6 }
最后的代碼應該是這樣的:
1 void mouse_callback(GLFWwindow* window, double xpos, double ypos) 2 { 3 if(firstMouse) 4 { 5 lastX = xpos; 6 lastY = ypos; 7 firstMouse = false; 8 } 9 10 float xoffset = xpos - lastX; 11 float yoffset = lastY - ypos; 12 lastX = xpos; 13 lastY = ypos; 14 15 float sensitivity = 0.05; 16 xoffset *= sensitivity; 17 yoffset *= sensitivity; 18 19 yaw += xoffset; 20 pitch += yoffset; 21 22 if(pitch > 89.0f) 23 pitch = 89.0f; 24 if(pitch < -89.0f) 25 pitch = -89.0f; 26 27 glm::vec3 front; 28 front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); 29 front.y = sin(glm::radians(pitch)); 30 front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); 31 cameraFront = glm::normalize(front); 32 }
三、縮放
實現一個縮放(Zoom)接口。在之前的教程中我們說視野(Field of View)或fov定義了我們可以看到場景中多大的范圍。當視野變小時,場景投影出來的空間就會減小,產生放大(Zoom In)了的感覺。我們會使用鼠標的滾輪來放大。與鼠標移動、鍵盤輸入一樣,我們需要一個鼠標滾輪的回調函數:
1 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) 2 { 3 if(fov >= 1.0f && fov <= 45.0f) 4 fov -= yoffset; 5 if(fov <= 1.0f) 6 fov = 1.0f; 7 if(fov >= 45.0f) 8 fov = 45.0f; 9 }
當滾動鼠標滾輪的時候,yoffset值代表我們豎直滾動的大小。當scroll_callback函數被調用后,我們改變全局變量fov變量的內容。因為45.0f
是默認的視野值,我們將會把縮放級別(Zoom Level)限制在1.0f
到45.0f
。
我們現在在每一幀都必須把透視投影矩陣上傳到GPU,但現在使用fov變量作為它的視野:
projection = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);
最后不要忘記注冊鼠標滾輪的回調函數:
glfwSetScrollCallback(window, scroll_callback);
?