Геометрическое решение пересечения сферических лучей GLSL

#c #opengl #glsl #shader #raytracing

#c #opengl #glsl #шейдер #трассировка лучей

Вопрос:

Я пытаюсь реализовать пересечение сферических лучей в GLSL, как геометрическое, так и аналитическое решение. У меня возникли проблемы с решением geom one, это должно иметь какое-то отношение к тому, как я возвращаю 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 amp;amp; t0 > t_min) {
        t_out = t0;
        return true;
    }

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

введите описание изображения здесь

Я думаю, что проблема заключается в том, как я справляюсь с 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 amp;amp; t_out > t_min) {
        return true;
    }
}

return false;
  

Аналитический результат

Комментарии:

1.1) Удалите строку if(tca < 0) return false; , в которой знак точки-p здесь не используется. 2) ray.direction должно быть нормализовано. 3) Как вы предварительно вычисляете t_min и t_max ?

2. @Ripi2 1) это создает странный оттенок на моих сферах, не уверен почему. 2) это почти исправляет проблему, кажется, не хватает тени между двумя сферами. 3) они жестко закодированы в ‘0.001’ и ‘9999.99’. Я обновлю сообщение некоторыми фотографиями.

Ответ №1:

Проблема вызвана 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 amp;amp; t0 > t_min) {
        t_out = t0 / length(ray.direction); // <---
        return true;
    } 

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

    return false; 
}