2014-09-28 3 views
19

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

Как вы можете видеть, есть центральная сфера (без зеркальной подсветки), отражающая сфера (справа) и преломляющая сфера (слева). Я очень доволен размышлениями, это выглядит очень хорошо. Для преломлений его любопытная работа ... свет преломляется, и все сферы сфер видны в сфере (показатель преломления 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: Я также изменил показатель преломления around.From

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 с помощью г ++ 4.8.2.

+1

Касательно: не злоупотребляйте перегрузками оператора! –

+1

Ваша вторая версия выглядит правильно - ожидается преломление, создающее инвертированное изображение. –

+0

Чтобы откликнуться на комментарий @OliverCharlesworth, я бы сказал, что использование 'operator |' делает * этот * код намного читабельнее и, следовательно, здесь не злоупотребляют * здесь *. Лучше быть осторожным с приоритетом и использовать эти '()' везде с ним. – hyde

ответ

14

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

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

enter image description here

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

enter image description here

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

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

enter image description here

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

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

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

enter image description here

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

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

+0

Удивительный вопрос, удивительный ответ ... – Mehrdad

+0

Отличный ответ, спасибо! – Koto

Смежные вопросы