Подтвердить что ты не робот

Преломление в Raytracing?

Я снова работаю над своим raytracer. Я добавил поддержку рефлексии и многопоточности. В настоящее время я работаю над добавлением преломлений, но его половина работает. Spheres

Как вы можете видеть, есть центральная сфера (без зеркальной подсветки), отражающая сфера (справа) и преломляющая сфера (слева). Я очень доволен размышлениями, это выглядит очень хорошо. Для преломлений его любопытная работа... свет преломляется, и все сферы сфер видны в сфере (показатель преломления 1.4), но есть внешнее черное кольцо.

EDIT: По-видимому, черное кольцо становится больше, и поэтому сфера меньше, когда я увеличиваю показатель преломления сферы. Напротив, при уменьшении показателя преломления Сфера становится больше, а черное кольцо меньше... до тех пор, пока показатель преломления не станет равным единице, кольцо полностью исчезнет. IOR = 1,9 enter image description here IOR = 1,1 enter image description here IOR = 1.00001 enter image description here И интересно, что при IOR = 1 сфера теряет прозрачность и становится белой. enter image description here

Я думаю, что я рассмотрел полное внутреннее отражение, и здесь это не проблема.

Теперь код: Я использую operator | для точечного продукта, поэтому (vec|vec) является точечным продуктом и operator ~ для инвертирования векторов. Объекты, как лиги, так и сферы, хранятся в Object **objects;. Функция Raytrace

Colour raytrace(const Ray &r, const int &depth)
{
    //first find the nearest intersection of a ray with an object
    Colour finalColour = skyBlue *(r.getDirection()|Vector(0,0,-1)) * SKY_FACTOR;
    double t, t_min = INFINITY;
    int index_nearObj = -1;
    for(int i = 0; i < objSize; i++)
    {
        if(!dynamic_cast<Light *>(objects[i]))//skip light src
        {
            t = objects[i]->findParam(r);
            if(t > 0 && t < t_min)
            {
                t_min = t;
                index_nearObj = i;
            }
        }
    }
    //no intersection
    if(index_nearObj < 0)
        return finalColour;

    Vector intersect = r.getOrigin() + r.getDirection()*t_min;
    Vector normal = objects[index_nearObj]->NormalAtIntersect(intersect);
    Colour objectColor = objects[index_nearObj]->getColor();
    Ray rRefl, rRefr; //reflected and refracted Ray
    Colour refl = finalColour, refr = finalColour; //reflected and refracted colours
    double reflectance = 0, transmittance = 0;

    if(objects[index_nearObj]->isReflective() && depth < MAX_TRACE_DEPTH)
    {
        //handle reflection
        rRefl = objects[index_nearObj]->calcReflectingRay(r, intersect, normal);
        refl = raytrace(rRefl, depth + 1);
        reflectance = 1;
    }

    if(objects[index_nearObj]->isRefractive() && depth < MAX_TRACE_DEPTH)
    {
        //handle transmission
        rRefr = objects[index_nearObj]->calcRefractingRay(r, intersect, normal, reflectance, transmittance);
        refr = raytrace(rRefr, depth + 1);
    }

    Ray rShadow; //shadow ray
    bool shadowed;
    double t_light = -1;

    Colour localColour;
    Vector tmpv;

    //get material properties
    double ka = 0.2; //ambient coefficient
    double kd; //diffuse coefficient
    double ks; //specular coefficient

    Colour ambient = ka * objectColor; //ambient component
    Colour diffuse, specular;
    double brightness;
    localColour = ambient;
    //look if the object is in shadow or light
    //do this by casting a ray from the obj and
    // check if there is an intersection with another obj
    for(int i = 0; i < objSize; i++)
    {
        if(dynamic_cast<Light *>(objects[i])) //if object is a light
        {
            //for each light
            shadowed = false;
            //create Ray to light
            tmpv = objects[i]->getPosition() - intersect;
            rShadow = Ray(intersect  + (!tmpv) * BIAS, tmpv);
            t_light = objects[i]->findParam(rShadow);

            if(t_light < 0) //no imtersect, which is quite impossible
                continue;

            //then we check if that Ray intersects one object that is not a light
            for(int j = 0; j < objSize; j++)
            {
                    if(!dynamic_cast<Light *>(objects[j]) && j != index_nearObj)//if obj is not a light
                    {
                        t = objects[j]->findParam(rShadow);
                        //if it is smaller we know the light is behind the object
                        //--> shadowed by this light
                        if (t >= 0 && t < t_light)
                        {
                            // Set the flag and stop the cycle
                            shadowed = true;
                            break;
                        }
                    }
            }

            if(!shadowed)
            {
                rRefl = objects[index_nearObj]->calcReflectingRay(rShadow, intersect, normal);
                //reflected ray from ligh src, for ks
                kd = maximum(0.0, (normal|rShadow.getDirection()));
                if(objects[index_nearObj]->getShiny() <= 0)
                    ks = 0;
                else
                    ks = pow(maximum(0.0, (r.getDirection()|rRefl.getDirection())), objects[index_nearObj]->getShiny());
                diffuse = kd * objectColor;// * objects[i]->getColour();
                specular = ks * objects[i]->getColor();
                brightness = 1 /(1 + t_light * DISTANCE_DEPENDENCY_LIGHT);
                localColour += brightness * (diffuse + specular);
            }
        }
    }
    finalColour = localColour + (transmittance * refr + reflectance * refl);
    return finalColour;
}

Теперь функция, которая вычисляет преломленный луч, я использовал несколько разных сайтов для ресурса, и у каждого были схожие алгоритмы. Это лучшее, что я мог сделать до сих пор. Это может быть крошечная деталь, которую я не вижу...

Ray Sphere::calcRefractingRay(const Ray &r, const Vector &intersection,Vector &normal, double & refl, double &trans)const
{
    double n1, n2, n;
    double cosI = (r.getDirection()|normal);
    if(cosI > 0.0)
    {
        n1 = 1.0;
        n2 = getRefrIndex();
        normal = ~normal;//invert
    }
    else
    {
        n1 = getRefrIndex();
        n2 = 1.0;
        cosI = -cosI;
    }
    n = n1/n2;
    double sinT2 = n*n * (1.0 - cosI * cosI);
    double cosT = sqrt(1.0 - sinT2);
    //fresnel equations
    double rn = (n1 * cosI - n2 * cosT)/(n1 * cosI + n2 * cosT);
    double rt = (n2 * cosI - n1 * cosT)/(n2 * cosI + n2 * cosT);
    rn *= rn;
    rt *= rt;
    refl = (rn + rt)*0.5;
    trans = 1.0 - refl;
    if(n == 1.0)
        return r;
    if(cosT*cosT < 0.0)//tot inner refl
    {
        refl = 1;
        trans = 0;
        return calcReflectingRay(r, intersection, normal);
    }
    Vector dir = n * r.getDirection() + (n * cosI - cosT)*normal;
    return Ray(intersection + dir * BIAS, dir);
}

EDIT: я также изменил показатель преломления. Из

    if(cosI > 0.0)
    {
        n1 = 1.0;
        n2 = getRefrIndex();
        normal = ~normal;
    }
    else
    {
        n1 = getRefrIndex();
        n2 = 1.0;
        cosI = -cosI;
    }

к

if(cosI > 0.0)
{
    n1 = getRefrIndex();
    n2 = 1.0;
    normal = ~normal;
}
else
{
    n1 = 1.0;
    n2 = getRefrIndex();
    cosI = -cosI;
}

Тогда я получаю это и почти то же (все еще вверх ногами) с показателем преломления в 1! enter image description here И расчет отражения:

Ray Sphere::calcReflectingRay(const Ray &r, const Vector &intersection, const Vector &normal)const
{
    Vector rdir = r.getDirection();
    Vector dir = rdir - 2 * (rdir|normal) * normal;
    return Ray(intersection + dir*BIAS, dir);
    //the Ray constructor automatically normalizes directions
}

Итак, мой вопрос: как исправить внешний черный круг? Какая версия верна?

Помощь с благодарностью:)

Это скомпилировано в Linux с помощью g++ 4.8.2.

4b9b3361

Ответ 1

Предупреждение: следующее предположение, а не определенность. Мне нужно будет посмотреть на код более подробно, чтобы убедиться, что происходит и почему.

Тем не менее, мне кажется, что ваш оригинальный код в основном имитирует вогнутую линзу вместо выпуклой.

enter image description here

Выпуклая линза представляет собой в основном увеличительную линзу, принося лучи света с относительно небольшой площади в фокус на плоскости:

enter image description here

Это также показывает, почему исправленный код показывает перевернутое изображение. Лучи света, идущие сверху с одной стороны, проецируются снизу на другую (и наоборот).

Возвращение к вогнутой линзе: вогнутая линза - это уменьшающая линза, которая показывает широкий угол изображения перед объективом:

enter image description here

Если вы посмотрите на нижний правый угол здесь, это показывает, что я подозреваю, это проблема: особенно с высоким показателем преломления, лучи света, пытающиеся проникнуть в объектив, пересекают край самого объектива. Для всех углов шире, чем вы, вы обычно увидите черное кольцо, потому что передняя кромка объектива действует как тень, чтобы предотвратить попадание света.

Увеличение показателя преломления увеличивает ширину этого черного кольца, поскольку свет больше изогнут, поэтому большая часть на краях пересекает внешний край линзы.

Если вам небезразлично, как они избегают этого с вещами, такими как широкоугольные объективы камеры, обычным путем является использование менискового объектива, по крайней мере для переднего элемента:

enter image description here

Это не панацея, но, по крайней мере, предотвращает пересечение входящих световых лучей от внешнего края переднего линзового элемента. В зависимости от того, насколько широкий угол, который должен покрывать объектив, он часто будет немного менее радикальным из мениска, чем это (и в некоторых случаях это будет планово-вогнутое), но вы получите общую идею.

Заключительное предупреждение: конечно же, все это нарисовано вручную и предназначено только для того, чтобы дать общее представление, а не (например) отражать дизайн любого конкретного объектива, элемент с каким-либо конкретным показателем преломления и т.д.