You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

二次公式在游戏编程中的应用:球体线段相交检测代码解析

Hey there! Let's break down exactly why this code uses the quadratic formula for sphere intersection checks—it's all about translating geometry into math, which is such a common pattern in game development. Let's start from the basics so you can fully grasp it and reuse the logic confidently.

Understanding Sphere-Segment Intersection with the Quadratic Formula

First: The Geometric Problem Translated to Math

This code checks if a line segment (from startPoint to endPoint) intersects a sphere (with radius m_radius, and crucially—this code assumes the sphere is centered at the origin (0,0,0); we'll come back to this later).

To find intersections, we use two key equations:

  1. Segment Parameterization: Any point along the segment can be written as:
    P(t) = startPoint + t*(endPoint - startPoint)
    Here, t is a scalar where:

    • t=0 = the start point
    • t=1 = the end point
    • Values between 0 and 1 are points on the segment itself.
  2. Sphere Equation: For a sphere centered at the origin with radius r, any point (x,y,z) on the sphere satisfies:
    x² + y² + z² = r²

Combine the Equations

If we substitute the segment's parameterized point into the sphere equation, we get a quadratic equation in terms of t. Let's define two vectors to simplify:

  • p = startPoint (vector from sphere origin to segment start)
  • v = endPoint - startPoint (direction vector of the segment)

Substituting P(t) = p + t*v into the sphere equation gives:
(p₀ + t*v₀)² + (p₁ + t*v₁)² + (p₂ + t*v₂)² = m_radius²

When you expand and rearrange this, it becomes the standard quadratic form a*t² + b*t + c = 0, where:

  • a = v₀² + v₁² + v₂² → This is the squared length of vector v
  • b = 2*(p₀*v₀ + p₁*v₁ + p₂*v₂) → Twice the dot product of vectors p and v
  • c = p₀² + p₁² + p₂² - m_radius² → Squared length of p minus squared sphere radius

Which is exactly what the code calculates for a, b, and c!

What the Discriminant (disc) Tells Us

The quadratic formula solves a*t² + b*t + c = 0 with t = [-b ± √(b²-4ac)]/(2a). The term inside the square root—disc = b²-4ac—is the discriminant, and it tells us everything about the intersection:

  • disc < 0: No real solutions → The segment doesn't intersect the sphere at all (code returns false).
  • disc = 0: Exactly one real solution → The segment is tangent to the sphere (touches at exactly one point).
  • disc > 0: Two real solutions → The segment passes through the sphere, entering and exiting at two points.

Breaking Down the Code Logic

Let's walk through the code step by step:

  1. Define Vectors:

    hduVector3Dd p = startPoint; hduVector3Dd v = endPoint - startPoint;
    

    p is the vector from the sphere's origin to the segment start; v is the direction of the segment.

  2. Calculate Quadratic Coefficients:
    The a, b, c variables match exactly the math we derived above.

  3. Check Intersection Possibility:
    Compute disc to see if there are real solutions. If disc < 0, return false immediately—no intersection.

  4. Solve for t (called n in the code):

    • If disc = 0, take the single solution n = -b/(2a).
    • If disc > 0, calculate both solutions and pick the smaller one—this is the first intersection point as you move from the segment's start to end.
  5. Validate the Solution:
    The code checks if n is between 0 and 1:

    • n > 1: The intersection is beyond the segment's end point.
    • n < 0: The intersection is behind the segment's start point.
      Either case means the segment doesn't intersect the sphere, so return false.

Reusing This Code (With Improvements!)

The original code has a key assumption: the sphere is centered at the origin. To reuse it for spheres anywhere in space, adjust the p vector to be relative to the sphere's center:

hduVector3Dd p = startPoint - sphereCenter; // sphereCenter is your sphere's actual center
hduVector3Dd v = endPoint - startPoint;

For a more robust reusable function, add handling for floating-point precision and edge cases (like a zero-length segment):

#include <cmath>
#include <algorithm> // for std::min, std::max, std::clamp

bool doesSegmentIntersectSphere(hduVector3Dd start, hduVector3Dd end, hduVector3Dd sphereCenter, double sphereRadius, double &outIntersectionT) {
    hduVector3Dd p = start - sphereCenter;
    hduVector3Dd v = end - start;
    
    double a = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
    // Handle zero-length segment (start == end)
    if (a < 1e-9) {
        double distSq = p[0]*p[0] + p[1]*p[1] + p[2]*p[2];
        if (distSq <= sphereRadius*sphereRadius + 1e-9) {
            outIntersectionT = 0.0;
            return true;
        }
        return false;
    }
    
    double b = 2 * (p[0]*v[0] + p[1]*v[1] + p[2]*v[2]);
    double c = p[0]*p[0] + p[1]*p[1] + p[2]*p[2] - sphereRadius * sphereRadius;
    double disc = b*b - 4*a*c;
    
    // Account for floating-point precision errors
    if (disc < -1e-9) return false;
    disc = std::max(disc, 0.0);
    
    double n;
    if (disc <= 1e-9) {
        n = (-b)/(2*a);
    } else {
        double posN = (-b + sqrt(disc))/(2*a);
        double negN = (-b - sqrt(disc))/(2*a);
        n = std::min(posN, negN);
    }
    
    // Check if n is within segment bounds, with precision tolerance
    if (n < -1e-9 || n > 1.0 + 1e-9) {
        // If the smaller t is out of bounds, check the larger one
        if (disc > 1e-9) {
            double otherN = std::max(posN, negN);
            if (otherN >= -1e-9 && otherN <= 1.0 + 1e-9) {
                outIntersectionT = std::clamp(otherN, 0.0, 1.0);
                return true;
            }
        }
        return false;
    }
    
    outIntersectionT = std::clamp(n, 0.0, 1.0);
    return true;
}

This function returns true if the segment intersects the sphere, and outputs the t value (for the parameterized segment) where the intersection happens.

内容的提问来源于stack exchange,提问作者KIM CHANGJUN

火山引擎 最新活动