Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ray-sphere intersection method not working

public double intersect(Ray r)
{
    double t;

    Vector L = r.origin.sub(pos);

    double a = r.direction.dot(r.direction);
    double b = 2*(r.direction.dot(L));
    double c = (L.dot(L)) - (radius*radius);

    double disc = b*b - 4*a*c;

    if (disc < 0)
        return -1;

    double distSqrt = Math.sqrt(disc);
    double q;

    if (b < 0)
        q = (-b - distSqrt)/2;
    else
        q = (-b + distSqrt)/2;

    double t0 = q/a;
    double t1 = c/q;

    if (t0 > t1)
    {
        double temp = t0;
        t0 = t1;
        t1 = temp;
    }

    if (t1 < 0)
        return -1;

    if (t0 < 0)
        t = t1;
    else
        t = t0;

    return t;
}

It should return -1 when there is no intersection.

There is a sphere at (5,0,0) with a radius of 2. When I pass a ray with origin (0,0,0) and direction (5,0,0).unit, it returns 3 as it should. When I pass a ray with origin (0,0,0) and direction (5,2,0).unit, it returns 3.9 as it should. When I pass a ray with origin (0,0,0) and direction (5,0,1).unit, it returns -1, even though the ray intersects. When the direction is (5,0,-1).unit, it returns 2.73, even though it is not possible for t to be less than 3 and it should return the same thing as (5,0,1).unit returns.

like image 726
user3538141 Avatar asked Nov 17 '25 20:11

user3538141


1 Answers

I think the condition used to calculate q is not right:

if (b < 0)
    q = (-b - distSqrt)/2;
else
    q = (-b + distSqrt)/2;

Here, you're deciding upon the sign of b. You should calculate both the values. It's clear that the first one, (-b - distSqrt)/2 would always give the smaller value of q, because distSqrt is always non-negative. Only if (-b - distSqrt)/2 is negative, you should check (-b + distSqrt)/2 which might be positive in some cases. This case will appear when the origin of the ray is located inside the sphere.

And is the t1 = c/q thing necessary? When you have the smaller positive value of q, you are done (because a must be positive, and 1 if the direction is an unit vector).

In my implementation, I did the calculation this way:

double Sphere::getIntersection(Ray ray)
{
    Vector v = ray.origin - center;
    double b = 2 * dot(ray.dir, v);
    double c = dot(v, v) - radius * radius;
    double d = b * b - 4 * c;
    if(d > 0)
    {
        double x1 = (-b - sqrt(d)) / 2;
        double x2 = (-b + sqrt(d)) / 2;
        if(x1 >= 0 && x2 >= 0) return x1;
        if(x1 < 0 && x2 >= 0) return x2;
    }
    return -1;
}

and it worked well.

like image 74
Sufian Latif Avatar answered Nov 20 '25 12:11

Sufian Latif



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!