2016-03-28 3 views
3

Я работаю над raytracer с использованием C++, и до сих пор я мог вычислить модель освещения на основе диффузных, зеркальных и окружающих компонентов. Моя проблема появилась, когда я пытался добавить тени моих сцен: сцены получить действительно перепутались:Неожиданный результат при вычислении теней в трассировщике луча

enter image description here

Мой код делится следующим образом:

  • У меня есть «SceneObject» базовый класс , который имеет виртуальный метод intersect(), который берет луч (определенный по происхождению и направлению) и выводит логические, а также возвращаемые аргументы для вычисленного значения t, hitpoint и нормали объекта.
  • Класс «Материал» содержит зеркальные и диффузные цвета (векторы), а также значение для показателя phong (int).
  • У меня есть 3 производных класса из вышеперечисленных классов SceneObject: plane, triangle и sphere, каждая из которых имеет собственную версию пересечения, определенную в базовом классе.
  • У меня есть функция, которая вычисляет выходной цвет для заданного пикселя с использованием нормали объекта, точки попадания, источника света и материала объекта.
  • Все мои объекты для рендеринга хранятся в векторе.

Вот один из моих производных классов, треугольник:

class Triangle: public SceneObject{ 
public: 
    Triangle (vec3 a, vec3 b, vec3 c, Material mat){ 
     name = "Triangle"; 
     p0 = a; 
     p1 = b; 
     p2 = c; 
     objectMaterial = mat; 
     normal = normalize(cross(p0-p1, p0-p2)); 
    } 

    //Möller-Trumbore algorithm 
    bool intersect(Ray aRay, float &t, vec3 &hitPoint, vec3 &n){//we will use ray-plane intersection and barycentric coords: 

     bool returnValue = false; 
     //first we need to get a t of intersection between the passed ray and the triangle 
     vec3 v0v1 = p1-p0; 
     vec3 v0v2 = p2-p0; 
     vec3 pvec = cross(aRay.getDirection(), v0v2); 

     float det = dot(v0v1, pvec); 

     if (det >= 1e-6){ // Only draw if not backfacing 

      float invDet = 1/det; 

      float u = dot(-p0, pvec) * invDet; 
      // No intersection if u < 0 or u > 1 
      if (u >=0 && u <= 1) { 
       vec3 qvec = cross(-p0, v0v1); 
       float v = dot(aRay.getDirection(), qvec) * invDet; 

       // No intersection if v < 0 or u + v > 1 
       if (v >=0 && (u + v) <= 1){ 
        t = dot(v0v2, qvec) * invDet; 
        returnValue = true; 

        hitPoint = aRay.getOrigin() + (t*aRay.getDirection()); 
        n = normal; 
        //calculated_Out_Colour = calculateOutputColour(normal, aRay, lightSource, objectMaterial, t, hitPoint); 

       } 
      } 
     } 

     return returnValue; 
    } 

private: 
    vec3 p0; 
    vec3 p1; 
    vec3 p2; 
    vec3 normal; 
}; 

И это мой основной цикл, где я генерировать все свои лучи для каждого пикселя моего окна, и определить цвет, и если ток позиция находится в тени или нет:

for(int i=0;i<imageBuffer.Height();i++){ 
for(int j=0;j<imageBuffer.Width();j++){ 
    float currentX = ((float)i-256); 
      float currentY = ((float)j-256); 
      //cout << currentX << ", " << currentY << ", " << currentZ << endl; 
      //make a ray for this pixel (i,j) 
      glm::vec3 rayDirection = glm::normalize(glm::vec3(currentX, currentY, -d)); 
    //make a ray for this pixel (i,j) 
    Ray currentRay(vec3(0,0,0), rayDirection); 

    vec3 hitPoint; 
    vec3 normalAtHit; 
    float tnear = 999; // closest intersection, set to INFINITY to start with 
    SceneObject* object = NULL; 
    for (int k = 0; k < objects.size(); k++) { 
     float t; // intersection to the current object if any 
     if (objects[k]->intersect(currentRay, t, hitPoint, normalAtHit) && t < tnear) { 
      object = objects[k].get(); 
      tnear = t; 

      vec3 shadowRayDirection = normalize(light1.getLightOrigin()-hitPoint); 
      Ray shadowRay(hitPoint+vec3(0.03, 0.03,0.03), shadowRayDirection); 
      float shadowT; 
      vec3 shadowHitPoint; 
      vec3 shadowN; 
      for (int m = 0; m < objects.size(); ++m) { 
       if (objects[m]->intersect(shadowRay, shadowT, shadowHitPoint, shadowN)) { 
        imageBuffer.SetPixel(i, j, ambientColour*ambientIntensity); 
        break; 
       } else { 
        imageBuffer.SetPixel(i, j, calculateOutputColour(normalAtHit, currentRay, light1, objects[k]->getMaterial(), hitPoint)); 
       } 
      } 
      } 
     } 
    } 
} 

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

enter image description here

Я признательна за любую помощь в попытке отладить эту вещь. Благодарю.

+1

Для вашего луча теней вы должны использовать «hitPoint + 0.03 * shadowRayDirection» или «hitPoint + 0.03 * normalAtHit' для источника луча (любой из них может работать, аргументы могут быть сделаны в любом случае). – Cornstalks

+0

исправил его до float shadowOffset = 0.05; Ray shadowRay (hitPoint + shadowOffset * shadowRayDirection, shadowRayDirection); , но он дает тот же результат :-( – jjcastil

+0

То, как вы вычисляете 't', может быть неправильным (но это может быть правильно, я не уверен). Я делаю это по-другому в моем лучом:' vec3 n = cross (v0v1, v0v2); float num = dot (n, p0 - aRay.getOrigin()); float den = dot (n, aRay.getDirection()); t = num/den; '(но проверьте, что' den! = 0' перед делением). Возможно, вы захотите попробовать. Одна вещь, которую вы можете сделать, чтобы проверить правильность значения 't', выводит изображение глубины, чтобы вы могли видеть, совпадают ли ваши расчетные точки пересечения с тем, что вы ожидая. – Cornstalks

ответ

0

Cornstalks является правильным, но важно одно, если вы используете hitPoint + 0.03 * normalAtHit, вы должны быть уверены, что нормальная соответствует тому, что соответствует падающему лучу, из того, что мы видим в вашем коде, вы просто берете нормальный вашего примитива, но каждая грань имеет 2 нормала (одна обращена от другой).

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

Для этого вы можете проверить точечный продукт вашего инцидентаRay с нормальным, в зависимости от результата (больше 0 или меньше 0), вы узнаете, если вам нужно изменить свое нормальное состояние.