<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 第六課:鍵盤和鼠標 歡迎來到第六課! 我們將學習如何通過鼠標和鍵盤來移動相機,就像在第一人稱射擊游戲中一樣。 ## 接口 這段代碼在整個課程中多次被使用,因此把它單獨放在一個文件中:common/controls.cpp,然后在common/controls.hpp中聲明函數接口,這樣tutorial06.cpp就能使用它們了。 和前節課比,tutorial06.cpp里的代碼變動很小。主要的變化是:每一幀都計算MVP(投影視圖矩陣)矩陣,而不像之前那樣只算一次。現在把這段代碼加到主循環中: ~~~ do{ // ... // Compute the MVP matrix from keyboard and mouse input computeMatricesFromInputs(); glm::mat4 ProjectionMatrix = getProjectionMatrix(); glm::mat4 ViewMatrix = getViewMatrix(); glm::mat4 ModelMatrix = glm::mat4(1.0); glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix; // ... } ~~~ 這段代碼需要3個新函數: - computeMatricesFromInputs()讀鍵盤和鼠標操作,然后計算投影視圖矩陣。這就是奇妙所在。 - getProjectionMatrix()返回計算好的投影矩陣。 - getViewMatrix()返回計算好的視圖矩陣。 這只是一種實現方式,當然,如果你不喜歡這些函數,勇敢地去改寫它們。 來看看controls.cpp在做什么。 ## 實際代碼 我們需要幾個變量。 ~~~ // position glm::vec3 position = glm::vec3( 0, 0, 5 ); // horizontal angle : toward -Z float horizontalAngle = 3.14f; // vertical angle : 0, look at the horizon float verticalAngle = 0.0f; // Initial Field of View float initialFoV = 45.0f; float speed = 3.0f; // 3 units / second float mouseSpeed = 0.005f; FoV is the level of zoom. 80° = very wide angle, huge deformations. 60° – 45° : standard. 20° : big zoom. ~~~ 首先根據輸入,重新計算位置,水平角,豎直角和視場角(FoV);再由它們算出視圖和投影矩陣。 ### 方向 讀取鼠標位置是容易的: ~~~ // Get mouse position int xpos, ypos; glfwGetMousePos(&xpos, &ypos); ~~~ 我們需要把光標放到屏幕中心,否則它將很快移到屏幕外,導致無法響應。 ~~~ // Reset mouse position for next frame glfwSetMousePos(1024/2, 768/2); ~~~ 注意:這段代碼假設窗口大小是1024*768,這不是必須的。你可以用glfwGetWindowSize來設定窗口大小。 計算觀察角度: ~~~ // Compute new orientation horizontalAngle += mouseSpeed * deltaTime * float(1024/2 - xpos ); verticalAngle += mouseSpeed * deltaTime * float( 768/2 - ypos ); ~~~ 從右往左閱讀這幾行代碼: - 1024/2 – xpos表示鼠標離窗口中心點的距離。這個值越大,轉動角越大。 - float(…)是浮點數轉換,使乘法順利進行 - mouseSpeed用來加速或減慢旋轉,可以隨你調整或讓用戶選擇。 - += : 如果你沒移動鼠標,1024/2-xpos的值為零,horizontalAngle+=0不改變horizontalAngle的值。如果你用的是”=”,每幀視角都被強制轉回到原始方向,這就不好了。 現在,在世界坐標系下計算一個向量,代表視線方向。 ~~~ // Direction : Spherical coordinates to Cartesian coordinates conversion glm::vec3 direction( cos(verticalAngle) * sin(horizontalAngle), sin(verticalAngle), cos(verticalAngle) * cos(horizontalAngle) ); ~~~ 這是一種標準計算,如果你不了解余弦和正弦,下面有一個簡短的解釋: ![](https://box.kancloud.cn/2015-11-02_5636f304e53de.gif) 上面的公式,只是上圖在三維空間下的推廣。 我們想算出相機的『上方向』。『上方向』不一定是Y軸正方向:你俯視時,『上方向』實際上是水平的。這里有一個例子,位置相同,視點相同的相機,卻有不同的『上方向』。 本例中,唯一不變的是,『相機的右邊』這個方向始終取水平方向。你可以試試:保持手臂水平伸直,向正上方看、向下看;向這之間的任何方向看(譯注:『看』立刻產生視線方向)。現在定義『右方向』向量:因為是水平的,故Y坐標為零,X和Z值就像上圖中的一樣,只是角度旋轉了90度,或Pi/2弧度。 ~~~ // Right vector glm::vec3 right = glm::vec3( sin(horizontalAngle - 3.14f/2.0f), 0, cos(horizontalAngle - 3.14f/2.0f) ); ~~~ 我們有一個『右方向』和一個視線方向,或者說是『前方向』。『上方向』垂直于這兩者。一個很有用的數學工具可以讓三者的聯系變得簡單:叉乘。 ~~~ // Up vector : perpendicular to both direction and right glm::vec3 up = glm::cross( right, direction ); ~~~ 叉乘是在做什么呢?很簡單,回憶第三課講到的右手定則。第一個向量是大拇指;第二個是食指;叉乘的結果就是中指。十分方便。 ### 位置 代碼十分直觀。順便說下,我用上/下/右/左鍵而不用wsad;是因為我的azerty鍵盤中,美式鍵盤的awsd鍵位處實際上是zqsd。qwerZ鍵盤其實又不一樣了,更別提韓國鍵盤了。我甚至不知道韓國人民用的鍵盤是什么布局,但我猜想肯定很不一樣。 ~~~ // Move forward if (glfwGetKey( GLFW_KEY_UP ) == GLFW_PRESS){ position += direction * deltaTime * speed; } // Move backward if (glfwGetKey( GLFW_KEY_DOWN ) == GLFW_PRESS){ position -= direction * deltaTime * speed; } // Strafe right if (glfwGetKey( GLFW_KEY_RIGHT ) == GLFW_PRESS){ position += right * deltaTime * speed; } // Strafe left if (glfwGetKey( GLFW_KEY_LEFT ) == GLFW_PRESS){ position -= right * deltaTime * speed; } ~~~ 這里唯一特別的是deltaTime。你不會希望每幀偏移1單元的,原因很簡單: - 如果你有一臺快電腦,每秒能跑60幀,你每秒移動60*speed個單位。 - 如果你有一臺慢電腦,每秒能跑20幀,你每秒移動20*speed個單位。 電腦性能不能成為速度不穩的借口;你需要通過“前一幀到現在的時間”或“時間間隔(deltaTime)”來控制移動步長。 - 如果你有一臺快電腦,每秒能跑60幀,你每幀移動1/60*speed個單位,每秒移動1*speed個單位。 - 如果你有一臺慢電腦,每秒能跑20幀,你每幀移動1/20*speed個單位,每秒移動1*speed個單位。 這就好多了。deltaTime很容易算: ~~~ double currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); ~~~ ### 視場角 為了好玩,我們可以把視場角綁定到鼠標滾輪,作為簡陋的縮放功能: ~~~ float FoV = initialFoV - 5 * glfwGetMouseWheel(); ~~~ ### 計算矩陣 計算矩陣已經很直觀了。使用和前面幾乎一樣的函數,僅參數不同。 ~~~ // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); // Camera matrix ViewMatrix = glm::lookAt( position, // Camera is here position+direction, // and looks here : at the same position, plus "direction" up // Head is up (set to 0,-1,0 to look upside-down) ); ~~~ ## 結果 ![](https://box.kancloud.cn/2015-11-02_5636f30500dd5.gif) ### 隱藏面消除 現在可以自由移動鼠標,你會注意到:如果鼠標移動到立方體里面,多邊形仍然會被顯示。這看起來理所當然,實則可以優化。事實上,在常見應用中,你從來不會處于立方體內。 有一個思路是讓GPU檢查相機在三角形的后面還是前面。如果在前面,顯示該三角形;如果相機在三角形后面,且不在網格(網格必須是封閉的)內部,那么必有其他三角形在相機前面,故不顯示該三角形。沒有人會注意到什么,除了一切都會變快:三角形平均少了兩倍! 更妙的是,檢查起來還很簡單:GPU計算三角形的法向(用叉乘,記得吧?),然后檢查這個法向是否朝向相機。 不幸的是這樣做有代價:三角形的方向是隱式的。這意味著如果你在緩沖區中交換兩個頂點,可能會產生洞。但一般來說,它值得做一點額外工作。一般你只要在三維建模軟件中點擊“反轉法向”(實際是交換兩個頂點,從而反轉法向),一切就正常了。 開啟隱藏面消除是很輕松的: ~~~ // Cull triangles which normal is not towards the camera glEnable(GL_CULL_FACE); ~~~ ## 練習 - 限制verticalAngle,使之不能顛倒方向 - 創建一個相機,使它繞著物體旋轉 ( position = ObjectCenter + ( radius * cos(time), height, radius * sin(time) ) );然后將半徑/高度/時間的變化綁定到鍵盤/鼠標上,諸如此類。 - 玩得開心!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看