Linux Inside
90
if(t < EPSILON) {
//
intersection behind the origin
return false;
}
//
fill the HitPoint structure
pt->obj = this;
pt->dist = t;
pt->pos = ray.origin + ray.dir * t;
pt->normal = normal;
return true;
}
Για την αναπαράσταση μίας σφαίρας θα χρειαστούμε απλά
την ακτίνα της και ένα διάνυσμα για το κέντρο.
Η σφαίρα με κέντρο (x y z) και ακτίνα r ορίζεται ως ο γεω-
μετρικός τόπος των σημείων που ικανοποιούν την εξίσωση
x
2
+ y
2
+ z
2
+ r
2
= 0.
Αντικαθιστώντας την παραμετρική εξίσωση της ακτίνας
στην εξίσωση της σφαίρας και κάνοντας μερικούς αλγεβρι-
κούς μετασχηματισμούς
(
σχήμα 2)
,
καταλήγουμε σε μία
απλή δευτεροβάθμια εξίσωση, που μπορεί να λυθεί ως προς
t με το γνωστό τρόπο.
Η λύση της δευτεροβάθμιας εξίσωσής μας δεν έχει πραγ-
ματικές λύσεις (αρνητική διακρίνουσα) όταν η ακτίνα δεν τέ-
μνει την σφαίρα, έχει μία λύση όταν η ακτίνα εφάπτεται στη
σφαίρα, ενώ όταν την τέμνει κανονικά, μας δίνει δύο λύσεις,
που αντιστοιχούν στην παραμετρική απόσταση της μπροστά
και πίσω τομής (από τις οποίες προφανώς εμάς μας ενδιαφέ-
ρει η κοντινότερη). Δείτε την υλοποίηση της συνάρτησης
Sphere::intersection στο αρχείο sphere.cc στο συνοδευτικό
κώδικα.
Τέλος, η κλάση Scene κρατάει σε std::vector όλα τα αντικεί-
μενα της σκηνής, τα φώτα που ορίζονται απλώς από ένα διά-
νυσμα θέσης, καθώς και την κάμερα που περιγράφεται από
έναν πίνακα μετασχηματισμού, ενώ δουλειά της είναι να υπο-
λογίζει το primary ray για κάθε pixel.
class Scene {
private:
std::vector<Object*> objects;
std::vector<Light*> lights;
Camera *camera;
public:
...
constructors/destructors κλπ ...
bool intersect(const Ray &ray, HitPoint *hit) const;
Color trace_ray(const Ray &ray, int rdepth) const;
Color shade(const Ray &ray, const HitPoint &hit, int
rdepth) const;
};
Το σημαντικότερο κομμάτι σε αυτή την κλάση είναι οι 3 συ-
ναρτήσεις που πρακτικά υλοποιούν σχεδόν όλο τον αλγόριθ-
μο του ray tracing. H trace_ray καλείται από το κεντρικό
rendering loop για κάθε pixel, ώστε να ακολουθήσει την ακτί-
να και να μας επιστρέψει το φως που λαμβάνουμε από αυτή
την κατεύθυνση, και άρα το χρώμα που πρέπει να βάψουμε
το pixel.
Για να το πετύχει αυτό, η trace_ray καλεί, κατ’ αρχάς, την
intersect για να βρει το σημείο που τέμνει η ακτίνα τη γεωμε-
τρία της σκηνής και, αν βρει τομή, καλεί τη shade με παράμε-
τρο τις πληροφορίες της τομής που είδαμε παραπάνω
(
HitPoint), για να υπολογίσει το χρώμα που πρέπει να επι-
στρέψει.
Η shade, με τη σειρά της, αφού υπολογίσει το φως που
λαμβάνει αυτό το σημείο από τις φωτεινές πηγές (lights
vector), μπορεί να ξανακινήσει τη διαδικασία recursively προς
την ανακλώμενη κατεύθυνση, καλώντας πάλι την trace_ray με
άλλη ακτίνα.
Color Scene::trace_ray(const Ray &ray, int rdepth) const
{
HitPoint hit;
if(intersect(ray, &hit)) {
return shade(ray, hit, rdepth);
}
//
not found, return background color
return bgcolor;
}
bool Scene::intersect(const Ray &ray, HitPoint
*
nearest_hit) const
{
nearest_hit->obj = 0;
nearest_hit->dist = DBL_MAX;
//
find the nearest hit (if any)
for(Object *obj: objects) {
HitPoint hit;
if(obj->intersect(ray, &hit) &&
hit.dist < nearest_hit->dist) {
*
nearest_hit = hit;
}
}
return nearest_hit->obj != 0;
}
Φωτισμός
Όπως είπαμε παραπάνω, η δουλειά της shade είναι να
υπολογίσει, κατ’ αρχάς, πόσο φως φτάνει από κάθε φωτεινή
πηγή σε κάποιο σημείο και πόσο από αυτό το φως ανακλάται
προς την κατεύθυνση από την οποία έρχεται η ακτίνα.
Για κάθε φωτεινή πηγή πρέπει πρώτα να διαπιστώσουμε αν
υπάρχει οπτική επαφή με την πηγή ή βρισκόμαστε σε σημείο
σκιασμένο από κάποιο ενδιάμεσο αντικείμενο. Για να το υπο-
λογίσουμε αυτό, αρκεί να ρίξουμε μία ακτίνα προς την κατεύ-
θυνση του φωτός (shadow ray) και να δούμε αν χτυπάει κά-
ποιο αντικείμενο ενδιάμεσα ή όχι. Αν βρούμε τομή, απλώς
Linux Labs – Ray Τracing
Σχήμα 2: Υπολογισμός τομής ακτίνας-σφαίρας.
2