博客專欄

        EEPW首頁 > 博客 > 3D 碰撞檢測

        3D 碰撞檢測

        發布人:ygtu 時間:2023-08-30 來源:工程師 發布文章
        推薦:使用NSDT場景編輯器快速搭建3D應用場景
        軸對齊邊界框

        與 2D 碰撞檢測一樣,軸對齊邊界框 (AABB) 是確定兩個游戲實體是否重疊的最快算法。這包括將游戲實體包裝在一個非旋轉(因此軸對齊)的框中,并檢查這些框在 3D 坐標空間中的位置以查看它們是否重疊。

        兩個漂浮在空間中的 3-D 非方形物體,被虛擬矩形框包圍。

        由于性能原因,存在軸對齊約束。兩個非旋轉框之間的重疊區域可以僅通過邏輯比較來檢查,而旋轉框需要額外的三角運算,這些操作的計算速度較慢。如果您有將要旋轉的實體,則可以修改邊界框的尺寸,使其仍環繞對象,或者選擇使用其他邊界幾何類型,例如球體(對旋轉不變)。下面的動畫 GIF 顯示了 AABB 的圖形示例,該示例調整其大小以適應旋轉實體。盒子不斷改變尺寸,以緊密貼合其中包含的實體。

        顯示虛擬矩形框的動畫旋轉節點隨著其中的節點旋轉而收縮和增長。盒子不旋轉。

        注意:查看 使用 THREE.js 進行邊界體積碰撞檢測 一文,了解此技術的實際實現。

        點 vs. AABB

        檢查一個點是否在 AABB 內非常簡單——我們只需要檢查點的坐標是否在 AABB 內;分別考慮每個軸。如果我們假設 P x、P y 和 Pz 是點的坐標,B minX–B maxX、B minY–B maxY 和 B minZB maxZ 是 AABB 每個軸的范圍,我們可以使用以下公式計算兩者之間是否發生了碰撞:

        或者在 JavaScript 中:

        .JS復制到剪貼板

        function isPointInsideAABB(point, box) {
          return (
            point.x >= box.minX &&
            point.x <= box.maxX &&
            point.y >= box.minY &&
            point.y <= box.maxY &&
            point.z >= box.minZ &&
            point.z <= box.maxZ
          );
        }
        AABB vs. AABB

        檢查一個 AABB 是否與另一個 AABB 相交類似于點測試。我們只需要使用框的邊界對每個軸進行一次測試。下圖顯示了我們將在 X 軸上執行的測試 — 基本上,范圍 A minX–A maxX 和 B minX–B maxX 是否重疊?

        Hand drawing of two rectangles showing the upper right corner of A overlapping the bottom left corner of B, as A's largest x coordinate is greater than B's smallest x coordinate.

        從數學上講,這看起來像這樣:

        在 JavaScript 中,我們會使用這個:

        .JS復制到剪貼板

        function intersect(a, b) {
          return (
            a.minX <= b.maxX &&
            a.maxX >= b.minX &&
            a.minY <= b.maxY &&
            a.maxY >= b.minY &&
            a.minZ <= b.maxZ &&
            a.maxZ >= b.minZ
          );
        }
        邊界球體

        使用邊界球體來檢測碰撞比 AABB 稍微復雜一些,但測試起來仍然相當快。球體的主要優點是它們對旋轉是不變的,因此如果包裹的實體旋轉,邊界球體仍將相同。它們的主要缺點是,除非它們要包裝的實體實際上是球形的,否則包裝通常不是很好的擬合(即用邊界球體包裹一個人會導致很多誤報,而 AABB 會是更好的匹配)。

        點與球體

        要檢查球體是否包含點,我們需要計算點和球心之間的距離。如果此距離小于或等于球體的半徑,則該點位于球體內部。

        手繪笛卡爾坐標系中球體和點的 2D 投影。該點在圓圈的右下角。距離由一條虛線表示,標記為 D,從圓的中心到點。較淺的線顯示從圓心到圓邊界的半徑,標記為 R。

        考慮到兩點 A 和 B 之間的歐幾里得距離為

        我們的點與球體碰撞檢測公式將如下所示:

        或者在 JavaScript 中:

        .JS復制到剪貼板

        function isPointInsideSphere(point, sphere) {
          // we are using multiplications because is faster than calling Math.pow
          const distance = Math.sqrt(
            (point.x - sphere.x) * (point.x - sphere.x) +
              (point.y - sphere.y) * (point.y - sphere.y) +
              (point.z - sphere.z) * (point.z - sphere.z),
          );
          return distance < sphere.radius;
        }

        注意:上面的代碼具有平方根,計算起來可能很昂貴。避免這種情況的簡單優化包括將平方距離與平方半徑進行比較,因此優化方程將涉及 。distanceSqr < sphere.radius * sphere.radius

        球體與球體

        球體與球體測試類似于點與球體測試。我們在這里需要測試的是球體中心之間的距離小于或等于它們的半徑之和。

        兩個部分重疊的圓的手繪。每個圓(不同大小)都有一條從其中心到邊界的光半徑線,標記為 R。距離用一條虛線表示,標記為 D,連接兩個圓的中心點。

        在數學上,這看起來像:

        或者在 JavaScript 中:

        .JS復制到剪貼板

        function intersect(sphere, other) {
          // we are using multiplications because it's faster than calling Math.pow
          const distance = Math.sqrt(
            (sphere.x - other.x) * (sphere.x - other.x) +
              (sphere.y - other.y) * (sphere.y - other.y) +
              (sphere.z - other.z) * (sphere.z - other.z),
          );
          return distance < sphere.radius + other.radius;
        }
        球體 vs. AABB

        測試球體和AABB是否碰撞稍微復雜一些,但仍然簡單快捷。一種合乎邏輯的方法是檢查 AABB 的每個頂點,對每個頂點進行點與球面測試。然而,這是矯枉過正的——測試所有頂點是不必要的,因為我們只需計算 AABB 的最近點(不一定是頂點)和球體中心之間的距離,看看它是否小于或等于球體的半徑。我們可以通過將球體的中心鉗制到 AABB 的極限來獲得此值。

        手繪一個正方形,部分重疊在圓的頂部。半徑由標記為 R 的淺線表示。距離線從圓的中心到正方形的最近點。

        在 JavaScript 中,我們會像這樣做這個測試:

        .JS復制到剪貼板

        function intersect(sphere, box) {
          // get box closest point to sphere center by clamping
          const x = Math.max(box.minX, Math.min(sphere.x, box.maxX));
          const y = Math.max(box.minY, Math.min(sphere.y, box.maxY));
          const z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ));
        
          // this is the same as isPointInsideSphere
          const distance = Math.sqrt(
            (x - sphere.x) * (x - sphere.x) +
              (y - sphere.y) * (y - sphere.y) +
              (z - sphere.z) * (z - sphere.z),
          );
        
          return distance < sphere.radius;
        }
        使用物理引擎

        3D物理引擎提供碰撞檢測算法,其中大多數也基于邊界體積。物理引擎的工作方式是創建一個物理身體,通常附加到它的視覺表示上。該主體具有速度、位置、旋轉、扭矩等屬性,以及物理形狀。此形狀是碰撞檢測計算中考慮的形狀。

        原文鏈接:3D 碰撞檢測 (mvrlink.com)


        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: 3dsmax 3D建模

        技術專區

        關閉
        主站蜘蛛池模板: 宜兰市| 安平县| 桑日县| 西安市| 宁陵县| 仪征市| 黑河市| 江山市| 临邑县| 盘锦市| 大同县| 中江县| 藁城市| 德昌县| 交口县| 望谟县| 大城县| 通山县| 融水| 佳木斯市| 西昌市| 彰化市| 湘西| 台中县| 梁山县| 蒲江县| 高陵县| 连山| 公主岭市| 中山市| 三穗县| 浦北县| 巧家县| 那坡县| 勐海县| 泰和县| 芷江| 涪陵区| 抚远县| 台江县| 全州县|