GLSL Sphere-射线相交几何解决方案

问题描述

我正在尝试在GLSL中实现球面射线相交,包括几何和解析解决方案。我在解决geom问题时遇到了麻烦,它应该与我返回true或false的方式有关:

bool hitSphere(Ray ray,Sphere sphere,float t_min,float t_max,out float t_out) {
    // Geometric solution
    float R2 = sphere.radius * sphere.radius;
    vec3 L = sphere.position - ray.origin;
    float tca = dot(L,normalize(ray.direction));
    // if(tca < 0) return false;

    float D2 = dot(L,L) - tca * tca;
    if(D2 > R2) return false;
    float thc = sqrt(R2 - D2);
    float t0 = tca - thc;
    float t1 = tca + thc;

    if(t0 < t_max && t0 > t_min) {
        t_out = t0;
        return true;
    }

    if(t1 < t_max && t1 > t_min) {
        t_out = t1;
        return true;
    }
    
    return false; 
}

enter image description here

我认为问题在于如何处理一个或两个交叉情况下的t0和t1。

编辑:有效的分析版本:

vec3 oc = ray.origin - sphere.position;
float a = dot(ray.direction,ray.direction);
float b = dot(oc,ray.direction);
float c = dot(oc,oc) - sphere.radius * sphere.radius;

float discriminant = b * b - a * c;

if (discriminant > 0.0f) {
    if(b > 0) 
        t_out = (-b + sqrt(discriminant)) / a;
    else 
        t_out = (-b - sqrt(discriminant)) / a;
    
    if(t_out < t_max && t_out > t_min) {
        return true;
    }
}

return false;

Analytical Result

解决方法

此问题是由t_out引起的。该算法必须以这种方式计算t_out,即X是射线与球体表面的相交点,对于:

X = ray.origin + ray.direction * t_out; 

在工作算法中,t_out取决于ray.direction的长度。如果向量t_out的大小较大,则ray.direction会变小。
在无效的算法中,ray.direction被标准化。

float tca = dot(L,normalize(ray.direction));

因此t_out是针对光线方向长度1计算的。实际上,您计算出t_out',其中t_out' = t_out * length(ray.direction)

t0分别t1除以ray.direction的长度:

bool hitSphere_2(Ray ray,Sphere sphere,float t_min,float t_max,out float t_out)
{
    float R2 = sphere.radius * sphere.radius;
    vec3 L = sphere.position - ray.origin;
    float tca = dot(L,normalize(ray.direction));
    // if(tca < 0) return false;

    float D2 = dot(L,L) - tca * tca;
    if(D2 > R2) return false;
    float thc = sqrt(R2 - D2);
    float t0 = tca - thc;
    float t1 = tca + thc;

    if (t0 < t_max && t0 > t_min) {
        t_out = t0 / length(ray.direction); // <---
        return true;
    } 

    if (t1 < t_max && t1 > t_min) {
        t_out = t1 / length(ray.direction); // <---
        return true;
    }

    return false; 
}