2016-03-28 4 views
3

Ich arbeite an einem Raytracer mit C++ und konnte bisher ein Beleuchtungsmodell basierend auf diffusen, spiegelnden und ambienten Komponenten berechnen. Mein Problem erschien, als ich versuchte Schatten meiner Szenen hinzuzufügen: die Szenen wirklich erhalten verkorkste:Unerwartetes Ergebnis beim Berechnen von Schatten in einem Raytracer

enter image description here

Mein Code wie folgt unterteilt:

  • Ich habe eine Basisklasse „Szenenobjekt“ , die eine virtuelle Methode intersect() hat, die einen Strahl (definiert durch Ursprung und Richtung) nimmt und einen booleschen Wert sowie Rückgabeargumente für den berechneten t-Wert, einen Trefferpunkt und die Normale des Objekts ausgibt.
  • Die Klasse "Material" enthält eine spiegelnde und diffuse Farbe (Vektoren) sowie einen Wert für einen Phong-Exponenten (int).
  • Ich habe 3 abgeleitete Klassen aus dem obigen SceneObject: Ebenen-, Dreieck- und Kugelklassen, jede mit ihrer eigenen Version des in der Basisklasse definierten Schnittpunkts.
  • Ich habe eine Funktion, die Ausgabefarbe für ein bestimmtes Pixel unter Verwendung der Normalen des Objekts, des Hitpoints, einer Lichtquelle und des Objektmaterials berechnet.
  • Alle meine Objekte zu rendern sind in einem Vektor gespeichert.

Hier ist eine meiner abgeleiteten Klassen, Dreieck:

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; 
}; 

Und das ist meine Hauptschleife, wo ich meine Strahlen alle für jedes Pixel von meinem Fenster erzeugen, und die Farbe zu bestimmen und wenn die aktuellen Position im Schatten befindet oder nicht:

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)); 
       } 
      } 
      } 
     } 
    } 
} 

ich bin ehrlich mit Verlust hier, und ich habe keine Ahnung, warum dies geschieht. Ich habe versucht, mit dem Algorithmus beschrieben here, aber es erzeugt das gleiche Ergebnis im Bild gezeigt. Als Referenz, wenn ich die Schleife nehmen und prüfen, ob Schatten, sieht meine Szene wie folgt aus:

enter image description here

schätzen ich Hilfe bei dem Versuch, diese Sache zu debuggen. Vielen Dank.

+1

Für Ihren Schattenstrahl sollten Sie 'hitPoint + 0.03 * shadowRayDirection' oder' hitPoint + 0.03 * normalAtHit' für den Ursprung des Strahls verwenden (jeder kann arbeiten; Argumente können in beide Richtungen erstellt werden). – Cornstalks

+0

korrigiert zu float shadowOffset = 0.05; Ray shadowRay (hitPoint + shadowOffset * shadowRayDirection, shadowRayDirection); aber es erzeugt das gleiche Ergebnis :-( – jjcastil

+0

Die Art, wie Sie 't' berechnen, könnte falsch sein (aber es könnte richtig sein; ich bin mir nicht sicher.) Ich mache es anders in meinem Raytracer:' vec3 n = cross (v0v1, v0v2); float num = Punkt (n, p0 - aRay.getOrigin()); float den = Punkt (n, aRay.getDirection()); t = num/den; '(aber überprüfe, dass' den! = 0 "vor dem Dividieren. Vielleicht möchten Sie das. Eine Sache, die Sie tun können, um zu testen, ob Ihr t-Wert stimmt, ist ein Tiefenbild, damit Sie sehen können, ob Ihre berechneten Schnittpunkte mit dem übereinstimmen, was Sie sind erwartet. – Cornstalks

Antwort

0

Cornstalks ist richtig, aber eine Sache wichtig, wenn Sie hitPoint + 0.03 * normalAtHit verwenden, müssen Sie sicher sein, dass die normale ist, die der einfallenden Strahl entspricht, von dem, was wir in Ihrem Code sehen Sie einfach das normale von Ihrem Primitiven, aber jedes Gesicht hat 2 Normale (eine umgekehrt).

Je nachdem, ob Sie einen Strahl haben, der von der falschen Seite startet und somit einen ungültigen Schatten verursachen kann.

Um dies zu tun, können Sie das Skalarprodukt Ihres IncidentRay mit dem normalen überprüfen, abhängig vom Ergebnis (größer als 0 oder kleiner als 0), werden Sie wissen, ob Sie Ihren normalen umkehren müssen.