Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 937521 of 3D collision detection

  • Revision slug: Games/Techniques/3D_collision_detection
  • Revision title: 3D collision detection
  • Revision id: 937521
  • Created:
  • Creator: ladybenko
  • Is current revision? No
  • Comment

Revision Content

In this set of articles, we will see different bounding volumes techniques to implement collision detection in 3D environments.

Axis-aligned Bounding-Boxes

As with 2D collision detection, axis-aligned bounding-boxes (AABB) are the quickest algorithm to determine whether two game entities are overlaping or not. It consists in wrapping game entities in a non-rotated (thus axis-aligned) box, and check overlapping between these boxes.

The axis-aligned constraint is there because of performance reasons. The overlapping between two non-rotated boxes can be check with just logical comparisons, whereas rotated boxes require trigonometry operations as well, which are slower. If you have entities that will be rotating, you can either modify the dimensions of the bounding-box so it still wraps the object, or opt for another bounding geometry, such as spheres (which are invariant to rotation). Here's a graphic example of an AABB that adapts its size to fit the rotating entity:

Note: If you are using Three.js, check out the Bounding Boxes with Three.js article.

Point versus AABB

Checking if a point is inside an AABB is just checking whether its coordinates fall inside each of the axis. Assuming Px, Py and Pz are the point's coordinates, and BminX, BmaxZ, etc. contain the limits of the AABB:

f(P,B)=(Px>=BminXPx<=BmaxX)(Py>=BminYPy<=BmaxY)(Pz>=BminZPz<=BmaxZ)f(P,B)= (P_x >= B_{minX} \wedge P_x <= B_{maxX}) \wedge (P_y >= B_{minY} \wedge P_y <= B_{maxY}) \wedge (P_z >= B_{minZ} \wedge P_z <= B_{maxZ})

Or in JavaScript:

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

AABB versus AABB

Checking whether an AABB intersects another AABB is similar to the point test. We just need to do one test per axis, using the boxes' boundaries. This figure shows the tests over the X axis.

updated version

Mathematically:

f(A,B)=(AminX<=BmaxXAmaxX>=BminX)(AminY<=BmaxYAmaxY>=BminY)(AminZ<=BmaxZAmaxZ>=BminZ)f(A,B) =

In JavaScript:

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);
}

Bounding spheres

Using bounding spheres to detect collisions is a bit more expensive than AABB, but still a quick test. The main advantage of spheres is that they are invariant to rotation, so if the wrapped entity rotates, the bounding sphere would still be the same.

Point versus sphere

To check whether an sphere contains a point we need to calculate the distance between the point and the sphere's center. If it's smaller or equal than the radius of the sphere, the point is inside it.

Taking into account, that the euclidean distance between two points A and B is (Ax-Bx)2)+(Ay-By)2+(Az-Bz)\sqrt{(A_x - B_x) ^ 2) + (A_y - B_y)^2 + (A_z - B_z)}:

f(P,S)=Sradius>=(Px-Sx)2+(Py-Sy)2+(Pz-Sz)2f(P,S) = S_{radius} >= \sqrt{(P_x - S_x)^2 + (P_y - S_y)^2 + (P_z - S_z)^2}

In JavaScript:

function isPointInsideSphere(point, sphere) {
  // we are using multiplications because is faster than calling Math.pow
  var 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;
}

The code above features an square root. An easy optimization to avoid it consists on squaring the radius instead. So the final sentence would involve distance < sphere.radius * sphere.radius.

Sphere versus sphere

The sphere versus sphere test is also similar to the point vs sphere. What we need to test instead is that the distance between the centers is less or equal than the sum of their radiuses.

Mathematically:

f(A,B)=(Ax-Bx)2+(Ay-By)2+(Az-Bz)2<=Aradius+Bradiusf(A,B) = \sqrt{(A_x - B_x)^2 + (A_y - B_y)^2 + (A_z - B_z)^2} <= A_{radius} + B_{radius}

In JavaScript:

function intersect(sphere, other) {
  // we are using multiplications because is faster than calling Math.pow
  var 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); }
}

Sphere versus AABB

Testing a sphere versus an AABB is slightly more complicated, but still simple and fast. A trivial approach would be checking every vertex of the AABB and do a point versus sphere test. However, testing all the vertices is unnecessary –we can get by with just the closest point (not necessarily a vertex) to the sphere's center. We can get this value by clamping the sphere's center to the AABB's limits.

In JavaScript:

function intersect(sphere, box) {
  // get box closest point to sphere center by clamping
  var x = Math.max(box.minX, Math.min(sphere.x, box.maxX);
  var y = Math.max(box.minY, Math.min(sphere.y, box.maxY);
  var z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ);

  // this is the same as isPointInsideSphere
  var 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;
}
 

See also

Related articles on MDN:

External resources:

Revision Source

<p class="summary">In this set of articles, we will see different bounding volumes techniques to implement collision detection in 3D environments.</p>

<h2 id="Axis-aligned_Bounding-Boxes">Axis-aligned Bounding-Boxes</h2>

<p>As with 2D collision detection, <strong>axis-aligned bounding-boxes</strong> (AABB) are the quickest algorithm to determine whether two game entities are overlaping or not. It consists in wrapping game entities in a non-rotated (thus axis-aligned) box, and check overlapping between these boxes.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/11797/Screen%20Shot%202015-10-16%20at%2015.11.21.png" style="display:block; height:275px; margin:0px auto; width:432px" /></p>

<p>The<strong> axis-aligned constraint</strong> is there because of performance reasons. The overlapping between two non-rotated boxes can be check with just logical comparisons, whereas rotated boxes require trigonometry operations as well, which are slower. If you have entities that will be rotating, you can either modify the dimensions of the bounding-box so it still wraps the object, or opt for another bounding geometry, such as spheres (which are invariant to rotation). Here's a graphic example of an AABB that adapts its size to fit the rotating entity:</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/11799/rotating_knot.gif" style="display:block; height:192px; margin:0px auto; width:207px" /></p>

<div class="note">
<p><strong>Note:</strong> If you are using Three.js, check out the <a href="/en-US/docs/Games/Techniques/3D_collision_detection/Bounding_boxes_with_THREE.js">Bounding Boxes with Three.js</a> article.</p>
</div>

<h3 id="Point_versus_AABB">Point versus AABB</h3>

<p>Checking if a point is inside an AABB is just checking whether its coordinates fall inside each of the axis. Assuming <em>P<sub>x</sub></em>, <em>P<sub>y</sub> </em>and <em>P<sub>z</sub></em> are the point's coordinates, and <em>B<sub>minX</sub></em>, <em>B<sub>maxZ</sub></em>, etc. contain the limits of the AABB:</p>

<p><math xmlns="https://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>P</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>x</mi></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>x</mi></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>y</mi></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>y</mi></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>z</mi></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>z</mi></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="TeX">f(P,B)= (P_x &gt;= B_{minX} \wedge P_x &lt;= B_{maxX}) \wedge (P_y &gt;= B_{minY} \wedge P_y &lt;= B_{maxY}) \wedge (P_z &gt;= B_{minZ} \wedge P_z &lt;= B_{maxZ})</annotation></semantics></math></p>

<p>Or in JavaScript:</p>

<pre class="brush: js">
function isPointInsideAABB(point, box) {
  return (point.x &gt;= box.minX &amp;&amp; point.x &lt;= box.maxX) &amp;&amp;
         (point.y &gt;= box.minY &amp;&amp; point.y &lt;= box.maxY) &amp;&amp;
         (point.z &gt;= box.minY &amp;&amp; point.z &lt;= box.maxZ);
}</pre>

<h3 id="AABB_versus_AABB">AABB versus AABB</h3>

<p>Checking whether an AABB intersects another AABB is similar to the point test. We just need to do one test per axis, using the boxes' boundaries. This figure shows the tests over the X axis.</p>

<p><img alt="updated version" src="https://mdn.mozillademos.org/files/11813/aabb_test.png" style="display:block; height:346px; margin:0px auto; width:461px" /></p>

<p>Mathematically:</p>

<p><math xmlns="https://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>A</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo>&lt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo>&gt;=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="TeX">f(A,B) =</annotation></semantics></math></p>

<p>In JavaScript:</p>

<pre class="brush: js">
function intersect(a, b) {
  return (a.minX &lt;= b.maxX &amp;&amp; a.maxX &gt;= b.minX) &amp;&amp;
         (a.minY &lt;= b.maxY &amp;&amp; a.maxY &gt;= b.minY) &amp;&amp;
         (a.minZ &lt;= b.maxZ &amp;&amp; a.maxZ &gt;= b.minZ);
}
</pre>

<h2 id="Bounding_spheres">Bounding spheres</h2>

<p>Using bounding spheres to detect collisions is a bit more expensive than AABB, but still a quick test. The main advantage of spheres is that they are invariant to rotation, so if the wrapped entity rotates, the bounding sphere would still be the same.</p>

<h3 id="Point_versus_sphere">Point versus sphere</h3>

<p>To check whether an sphere contains a point we need to calculate the distance between the point and the sphere's center. If it's smaller or equal than the radius of the sphere, the point is inside it.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/11803/point_vs_sphere.png" style="display:block; height:262px; margin:0px auto; width:385px" /></p>

<p>Taking into account, that the euclidean distance between two points <em>A</em> and <em>B</em> is <math xmlns="https://www.w3.org/1998/Math/MathML"><semantics><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>A</mi><mi>x</mi></msub><mo>-</mo><msub><mi>B</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>y</mi></msub><mo>-</mo><msub><mi>B</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>z</mi></msub><mo>-</mo><msub><mi>B</mi><mi>z</mi></msub><mo stretchy="false">)</mo></mrow></msqrt><annotation encoding="TeX">\sqrt{(A_x - B_x) ^ 2) + (A_y - B_y)^2 + (A_z - B_z)}</annotation></semantics></math>:</p>

<p><math xmlns="https://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>P</mi><mo>,</mo><mi>S</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>S</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub><mo>&gt;=</mo><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>P</mi><mi>x</mi></msub><mo>-</mo><msub><mi>S</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>y</mi></msub><mo>-</mo><msub><mi>S</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>z</mi></msub><mo>-</mo><msub><mi>S</mi><mi>z</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></msqrt></mrow><annotation encoding="TeX">f(P,S) = S_{radius} &gt;= \sqrt{(P_x - S_x)^2 + (P_y - S_y)^2 + (P_z - S_z)^2}</annotation></semantics></math></p>

<p>In JavaScript:</p>

<pre class="brush: js">
function isPointInsideSphere(point, sphere) {
  // we are using multiplications because is faster than calling Math.pow
  var 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 &lt; sphere.radius;
}
</pre>

<div class="note">
<p>The code above features an square root. An easy optimization to avoid it consists on squaring the radius instead. So the final sentence would involve <code>distance &lt; sphere.radius * sphere.radius</code>.</p>
</div>

<h3 id="Sphere_versus_sphere">Sphere versus sphere</h3>

<p>The sphere versus sphere test is also similar to the point vs sphere. What we need to test instead is that the distance between the centers is less or equal than the sum of their radiuses.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/11805/sphere_vs_sphere.png" style="display:block; height:262px; margin:0px auto; width:414px" /></p>

<p>Mathematically:</p>

<p><math xmlns="https://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>A</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>A</mi><mi>x</mi></msub><mo>-</mo><msub><mi>B</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>y</mi></msub><mo>-</mo><msub><mi>B</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>z</mi></msub><mo>-</mo><msub><mi>B</mi><mi>z</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></msqrt><mo>&lt;=</mo><msub><mi>A</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub><mo>+</mo><msub><mi>B</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub></mrow><annotation encoding="TeX">f(A,B) = \sqrt{(A_x - B_x)^2 + (A_y - B_y)^2 + (A_z - B_z)^2} &lt;= A_{radius} + B_{radius}</annotation></semantics></math></p>

<p>In JavaScript:</p>

<pre class="brush: js">
function intersect(sphere, other) {
  // we are using multiplications because is faster than calling Math.pow
  var 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 &lt; (sphere.radius + other.radius); }
}</pre>

<h3 id="Sphere_versus_AABB">Sphere versus AABB</h3>

<p>Testing a sphere versus an AABB is slightly more complicated, but still simple and fast. A trivial approach would be checking every vertex of the AABB and do a point versus sphere test. However, testing all the vertices is unnecessary –we can get by with just the <em>closest</em><em> point</em> (not necessarily a vertex) to the sphere's center. We can get this value by clamping the sphere's center to the AABB's limits.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/11805/sphere_vs_sphere.png" style="display:block; height:262px; margin:0px auto; width:414px" /></p>

<p>In JavaScript:</p>

<pre class="brush: js">
function intersect(sphere, box) {
  // get box closest point to sphere center by clamping
  var x = Math.max(box.minX, Math.min(sphere.x, box.maxX);
  var y = Math.max(box.minY, Math.min(sphere.y, box.maxY);
  var z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ);

  // this is the same as isPointInsideSphere
  var distance = Math.sqrt((x - sphere.x) * (x - sphere.x) +
                           (y - sphere.y) * (y - sphere.y) +
                           (z - sphere.z) * (z - sphere.z));
  
  return distance &lt; sphere.radius;
}
 
</pre>

<h2 id="See_also">See also</h2>

<p>Related articles on MDN:</p>

<ul>
 <li><a href="/en-US/docs/Games/Techniques/3D_collision_detection/Bounding_boxes_with_THREE.js">Bounding boxes with Three.js</a></li>
 <li><a href="/en-US/docs/Games/Techniques/2D_collision_detection">2D collision detection</a></li>
</ul>

<p>External resources:</p>

<ul>
 <li><a href="https://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php">Simple intersection tests for games</a> on Gamasutra</li>
 <li><a href="https://en.wikipedia.org/wiki/Bounding_volume">Bounding volume</a> on Wikipedia</li>
</ul>
Revert to this revision