2014-09-19 2 views
3

Итак, я прочитал о рейтрайсе в сети и начал писать Raytracer с нуля в свое свободное время. Я использую C++, который я изучал уже около месяца. Я прочитал о теории raytracing в сети, и до сих пор он работал достаточно хорошо. Его просто базовый Raytracer, который не использует ни модели, ни текстуры.Raytracing Shadows

Сначала он сделал Raycaster и был очень доволен результатом. enter image description here

Итак, я попробовал несколько объектов, и он тоже работал. Я просто использовал диффузное затенение в этой реализации и добавил к цвету свет к цвету объекта в тех точках, которые он не затенен. enter image description here К сожалению, этот код не работал для нескольких источников света. Итак, я начал переписывать свой код, чтобы он поддерживал несколько источников света. Я также читал на Фонг освещения и должен работать: enter image description here Он даже работает с несколькими огнями: enter image description here

Пока я доволен, но теперь мой я вроде застрял. Я пробовал исправлять это довольно долгое время, но я ничего не придумал. Когда я добавляю вторую Сферу или даже третью, загорается только последняя Сфера. К последнему я имею в виду объект в моем массиве, где храню все объекты. См. Ниже код. enter image description here

Очевидно, что фиолетовая Сфера должна иметь такое же освещение, поскольку их центр лежит в одной плоскости. К моему удивлению, в сфере только окружающее освещение -> заштриховано, чего не должно быть.

Так что теперь мой след функции:

Colour raytrace(const Ray &r, const int &depth) 
{ 
    //first find the nearest intersection of a ray with an object 
    //go through all objects an find the one with the lowest parameter 

    double t, t_min = INFINITY; 
    int index_nearObj = -1;//-1 to know if obj found 
    for(int i = 0; i < objSize; i++) 
    { 
     //skip light src 
     if(!dynamic_cast<Light *>(objects[i])) 
     { 
      t = objects[i]->findParam(r); 
      if(t > 0 && t < t_min) //if there is an intersection and 
      //its distance is lower than the current min --> new min 
      { 
       t_min = t; 
       index_nearObj = i; 
      } 
     } 
    } 
    //now that we have the nearest intersection, calc the intersection point 
    //and the normal at that point 
    //r.position + t * r.direction 

    if(t_min < 0 || t_min == INFINITY) //no intersection --> return background Colour 
     return White; 
    Vector intersect = r.getOrigin() + r.getDirection()*t; 
    Vector normal = objects[index_nearObj]->NormalAtIntersect(intersect); 

    //then calculate light ,shading and colour 


    Ray shadowRay; 
    Ray rRefl;//reflected ray 
    bool shadowed; 
    double t_light = -1; 
    Colour finalColour = White; 
    Colour objectColor = objects[index_nearObj]->getColour(); 
    Colour localColour; 
    Vector tmpv; 

    //get material properties 
    double ka = 0.1; //ambient coefficient 
    double kd; //diffuse coefficient 
    double ks; //specular coefficient 
    Colour ambient = ka * objectColor; //ambient component 
    //the minimum Colour the obj has, even if object is not hit by light 
    Colour diffuse, specular; 
    double brightness; 
    int index = -1; 
    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 
      //its origin is the intersection point 
      //its direction is the position of the light - intersection 
      tmpv = objects[i]->getPosition() - intersect; 
      shadowRay = Ray(intersect + (!tmpv) * BIAS, tmpv); 
      //the ray construcor automatically normalizes its direction 
      t_light = objects[i]->findParam(shadowRay); 



      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]))//if obj is not a light 
       { 
        //we compute the distance to the object and compare it 
        //to the light distance, for each light seperately 
        //if it is smaller we know the light is behind the object 
        //--> shadowed by this light 

        t = objects[j]->findParam(shadowRay); 
        if(t < 0) // no intersection 
         continue; 
        if(t < t_light) // intersection that creates shadow 
         shadowed = true; 
        else 
        { 
         shadowed = false; 
         index = j;//not using the index now,maybe later 
         break; 
        } 
       } 
      } 

      //we know if intersection is shadowed or not 
      if(!shadowed)// if obj is not shadowed 
      { 
       rRefl = objects[index_nearObj]->calcReflectingRay(shadowRay, intersect); //reflected ray from ligh src, for ks 
       kd = maximum(0.0, (normal|shadowRay.getDirection())); 
       ks = pow(maximum(0.0, (r.getDirection()|rRefl.getDirection())), objects[index_nearObj]->getMaterial().shininess); 
       diffuse = kd * objectColor;// * objects[i]->getColour(); 
       specular = ks * objects[i]->getColour();//not sure if obj needs specular colour 
       brightness = 1 /(1 + t_light * DISTANCE_DEPENDENCY_LIGHT); 
       localColour += brightness * (diffuse + specular); 
      } 
     } 
    } 
    //handle reflection 

    //handle transmission 

    //combine colours 
    //localcolour+reflectedcolour*refl_coeff + transmittedcolor*transmission coeff 

    finalColour = localColour; //+reflcol+ transmcol 
    return finalColour; 
} 

Следующая функция визуализации:

for(uint32_t y = 0; y < h; y++) 
{ 
    for(uint32_t x = 0; x < w; x++) 
    { 
     //pixel coordinates for the scene, depends on implementation...here camera on z axis 
     pixel.X() = ((x+0.5)/w-0.5)*aspectRatio *angle; 
     pixel.Y() = (0.5 - (y+0.5)/w)*angle; 
     pixel.Z() = look_at.getZ();//-1, cam at 0,0,0 

     rTmp = Ray(cam.getOrigin(), pixel - cam.getOrigin()); 
     cTmp = raytrace(rTmp, depth);//depth == 0 
     pic.setPixel(y, x, cTmp);//writes colour of pixel in picture 
    } 
} 

Так вот мои пересе- функции:

double Sphere::findParam(const Ray &r) const 
{ 
    Vector rorig = r.getOrigin(); 
    Vector rdir = r.getDirection(); 
    Vector center = getPosition(); 
    double det, t0 , t1; // det is the determinant of the quadratic equation: B² - 4AC; 

    double a = (rdir|rdir); 
    //one could optimize a away cause rdir is a unit vector 
    double b = ((rorig - center)|rdir)*2; 
    double c = ((rorig - center)|(rorig - center)) - radius*radius; 
    det = b*b - 4*c*a; 
    if(det < 0) 
     return -1; 

    t0 = (-b - sqrt(det))/(2*a); 
    if(det == 0)//one ontersection, no need to compute the second param!, is same 
     return t0; 

    t1 = (-b + sqrt(det))/(2*a); 
    //two intersections, if t0 or t1 is neg, the intersection is behind the origin! 

    if(t0 < 0 && t1 < 0) return -1; 
    else if(t0 > 0 && t1 < 0) return t0; 
    else if(t0 < 0 && t1 > 0) return t1; 
    if(t0 < t1) 
     return t0; 
    return t1; 
} 

Ray Sphere::calcReflectingRay(const Ray &r, const Vector &intersection)const 
{ 
    Vector rdir = r.getDirection(); 
    Vector normal = NormalAtIntersect(intersection); 
    Vector dir = rdir - 2 * (rdir|normal) * normal; 
    return Ray(intersection, !dir); 
} 

//Light intersection(point src) 
double Light::findParam(const Ray &r) const 
{ 
    double t; 
    Vector rorig = r.getOrigin(); 
    Vector rdir = r.getDirection(); 
    t = (rorig - getPosition())|~rdir; //~inverts a Vector 
    //if I dont do this, then both spheres are not illuminated-->ambient 
    if(t > 0) 
     return t; 
    return -1; 
} 

Вот это абстрактный класс Object , Каждая сфера, свет и т. Д. Является объектом.

class Object 
{ 
    Colour color; 
    Vector pos; 
    //Colour specular;not using right now 
    Material_t mat; 
public: 
    Object(); 
    Object(const Colour &); 
    Object(const Colour &, const Vector &); 
    Object(const Colour &, const Vector &, const Material_t &); 
    virtual ~Object()=0; 

    virtual double findParam(const Ray &) const =0; 
    virtual Ray calcReflectingRay(const Ray &, const Vector &)const=0; 
    virtual Ray calcRefractingRay(const Ray &, const Vector &)const=0; 
    virtual Vector NormalAtIntersect(const Vector &)const=0; 

    Colour getColour()const {return color;} 
    Colour & colour() {return color;} 
    Vector getPosition()const {return pos;} 
    Vector & Position() {return pos;} 
    Material_t getMaterial()const {return mat;} 
    Material_t & material() {return mat;} 

    friend bool operator!=(const Object &obj1, const Object &obj2) 
    {//compares only references! 
     if(&obj1 != &obj2) 
      return true; 
     return false; 
    } 
}; 

Я использую глобальный массив указателей объектов, чтобы хранить все огни, шары и т.д. от мира:

Object *objects[objSize]; 

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

EDIT 1 Я добавил фотографии.

EDIT 2 Обновлен код, исправлена ​​незначительная ошибка. Все еще нет решения.

Обновление: добавлен визуальный код, который создает лучи.

+1

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

+0

Кстати, использование 'operator |' для точечного продукта - не лучшая идея, из-за того, что он имеет неправильный приоритет и очень необычен. –

+0

И что такое 'operator!' В 'intersect +! (Объекты [i] -> getPosition() - пересекаются) * BIAS'? –

ответ

6

Проблема нашла

мне удалось отладить трассировки лучей с помощью Linux и GCC.
Что касается проблемы, ну ... однажды, когда я ее нашел, я почувствовал желание снова наброситься на мою клавиатуру.:) Ваш алгоритм является правильным, исключением для маленькой скрытой детали:

Vector intersect = r.getOrigin() + r.getDirection()*t; 

При расчете точки пересечения, можно использовать t вместо t_min.
Исправление заключается в изменении выше строку:

Vector intersect = r.getOrigin() + r.getDirection()*t_min; 

правильный вывод состоит в следующем:

enter image description here

Другие предложения

Я думаю, что проблема лежит в вашей теневой луч петля:

 //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]))//if obj is not a light 
      { 
       //we compute the distance to the object and compare it 
       //to the light distance, for each light seperately 
       //if it is smaller we know the light is behind the object 
       //--> shadowed by this light 

       t = objects[j]->findParam(shadowRay); 
       if(t < 0) // no intersection 
        continue; 
       if(t < t_light) // intersection that creates shadow 
        shadowed = true; 
       else 
       { 
        shadowed = false; 
        index = j;//not using the index now,maybe later 
        break; 
       } 
      } 
     } 

В принципе, когда обнаружено пересечение, вы устанавливаете флаг shadowed на true, НО вы продолжаете цикл: это и неэффективно, и неверно. Когда обнаружено пересечение, нет необходимости искать другого. Я предполагаю, что ваш флаг shadow снова установлен на false, потому что вы не останавливаете цикл. Кроме того, когда t >= t_light вы прерываете цикл, что также неверно (это точно так же, как t < 0). Я хотел бы изменить код на следующее:

  //then we check if that Ray intersects one object that is not a light 
      for (int j = 0; j < objSize; j++) 
      { 
       // Exclude lights AND the closest object found 
       if(j != index_nearObj && !dynamic_cast<Light *>(objects[j])) 
       { 
        //we compute the distance to the object and compare it 
        //to the light distance, for each light seperately 
        //if it is smaller we know the light is behind the object 
        //--> shadowed by this light 

        t = objects[j]->findParam(shadowRay); 

        // If the intersection point lies between the object and the light source, 
        // then the object is in shadow! 
        if (t >= 0 && t < t_light) 
        { 
          // Set the flag and stop the cycle 
          shadowed = true; 
          break; 
        } 
       } 
      } 

Некоторые другие предложения:

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

  • Не перегружайте операторы для точечного продукта и нормализации: используйте вместо этого специальные функции.

  • Постарайтесь как можно больше уменьшить объем ваших переменных: это улучшает читаемость кода.

  • Keep исследуя трассировку лучей материала, потому что это круто: D

+0

Мне нравится этот подход, определенно лучше, чем у меня, но он все еще не решает проблему с несколькими объектами ... Может быть, мои функции пересечения неправильны? Я не знаю ... но спасибо за помощь, я очень ценю это. Если вы заинтересованы, я могу отправить вам источник. – Koto

+0

Загрузите свой проект и свяжите его здесь, я попробую. – XDnl

+0

Отсутствует заголовок Matrix.h! (редактирование: однако кажется, что он не используется: P) – XDnl