sources:
BOX.CPP (3.1k)
BOX.H (507 bytes)
RAY.CPP (609 bytes)
RAY.H (604 bytes)
SPHERE.CPP (1.9k)
SPHERE.H (564 bytes)
TRIANGLE.CPP (2.1k)
TRIANGLE.H (1.2k)
UTIL.H (1.7k)
VECTOR.CPP (1.6k)
VECTOR.H (5.0k)
cgapplication.cpp (3.2k)
cgapplication.h (3.5k)
cgraytracer.cpp (18.8k)
cgraytracer.h (1.6k)
shape.h (754 bytes)


binaries:
Release/raytracer.exe (38.0k)


website:
more info here


screenshot:
studies/grafik2/Computergrafik-Code6/Aufgabe17/cgraytracer.cpp
download file

  1 #include "cgraytracer.h"
  2 #include <stdlib.h>
  3 #include <time.h>
  4
  5
  6 // watermark
  7 #pragma comment(exestr, "(C)2001-2002 by Stephan Brumme")
  8
  9
 10 // my parent's GPU driver is much too slow ... definitely !!!
 11 //#define DONT_DRAW
 12
 13
 14 static double PI = 3.1415926;
 15 static double INTERSECTION_OFFSET = 0.0001;
 16 static Color  clrBackground(0.05, 0.05, 0.1);
 17 inline double frand() { return double(rand()) / (RAND_MAX); }
 18
 19 //
 20 // CGRayTracer Application
 21 //
 22
 23
 24 CGRayTracer::CGRayTracer(unsigned int x, unsigned int y) {
 25    
 26     // general settings
 27     // use shadows ?
 28     shadows_ = true;
 29     // recursion depth
 30     maxDepth_ = 7;
 31     // no. of intersection tests
 32     nTests_ = 0;
 33     // initial detail level (no. of pixels per ray)
 34     detail_ = 1<<8;
 35     antialias_ = false;
 36
 37     // allocate memory to store the image
 38     imagex_ = x;
 39     imagey_ = y;
 40     image_  = new Color[(imagex_+1)*(imagey_+1)];
 41     for (unsigned int i=0; i<imagey_; i++)
 42         for (unsigned int j=0; j<imagex_; j++)
 43             image_[i*imagex_+j] = Color(-0.1,0,0);
 44
 45     // number of shapes
 46     shapeSize_ = 0; // incremented by AddObject
 47
 48     // create shape and material array
 49     shape_ = new Shape*[100];   
 50     material_ = new Material*[100];
 51
 52
 53
 54     // background
 55     AddObject(new Box(Vector(-1000, -1000, 4), Vector(1000, 1000, 4.1)),
 56               new Material (0.2, 0.5, 0.0, 10, 1, 1, Color(0.5,0.5,0.9)))
;
 57
 58     // lower surface
 59     AddObject(new Box(Vector(-1, -0.1, -1), Vector(1, 0, 1)),
 60               new Material (0.2, 0.5, 0.8, 10, 1, 1, Color(0.1,0.4,0.1)))
;
 61
 62     // main sphere
 63     AddObject(new Sphere (Vector(0,0,0), 0.5),
 64               new Material (0.2, 0.5, 0.7, 32, 0.3, 1, Color(0.9,0.9,0.9)))
;
 65
 66     // pyramid
 67     AddObject(new Triangle(Vector(0, 0, -0.4), Vector(0.4, 0, 0), Vector(0, 0.4, 0)),
 68               new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 69     AddObject(new Triangle(Vector(0, 0, -0.4), Vector(-0.4, 0, 0), Vector(0, 0.4, 0)),
 70               new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 71     AddObject(new Triangle(Vector(0, 0, +0.4), Vector(0.4, 0, 0), Vector(0, 0.4, 0)),
 72               new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 73     AddObject(new Triangle(Vector(0, 0, +0.4), Vector(-0.4, 0, 0), Vector(0, 0.4, 0)),
 74               new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 75
 76     // sphere on top of the pyramid
 77     AddObject(new Sphere (Vector(0,0.45,0), 0.08),
 78               new Material (0.2, 0.5, 0.99, 32, 1, 1, Color(1.0,0.3,0.3)))
;
 79
 80     // two arcs
 81     const int ARCSIZE = 14;
 82     for (i=0; i<ARCSIZE; i++)
 83     {
 84         const double arc_angle = ((i+0.5)*180.0 / (ARCSIZE-1)) * PI/180;
 85
 86         // arc 1
 87         AddObject(new Sphere (Vector(+cos(arc_angle)*0.6, sin(arc_angle)*0.8, cos(arc_angle)*0.6), 0.05),
 88                   new Material (0.2, 0.5, 0.7, 32, 0.3, 1.3, Color(0.8,0.8,0.8)))
;
 89         AddObject(new Sphere (Vector(+cos(arc_angle)*0.6, sin(arc_angle)*0.8, cos(arc_angle)*0.6), 0.01),
 90                   new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 91
 92         // arc 2
 93         AddObject(new Sphere (Vector(-cos(arc_angle)*0.6, sin(arc_angle)*0.8, cos(arc_angle)*0.6), 0.05),
 94                   new Material (0.2, 0.5, 0.7, 32, 0.3, 1.3, Color(0.8,0.8,0.8)))
;
 95         AddObject(new Sphere (Vector(-cos(arc_angle)*0.6, sin(arc_angle)*0.8, cos(arc_angle)*0.6), 0.01),
 96                   new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8)))
;
 97     }
 98
 99     // ring
100     const int RINGSIZE = 25;
101     for (i=0; i<RINGSIZE; i++)
102     {
103         const double ring_angle = (i * 360 / RINGSIZE) * PI/180;
104         AddObject(new Sphere (Vector(cos(ring_angle)*0.5, 0.07, sin(ring_angle)*0.5), 0.03),
105                   new Material (0.2, 0.5, 0.9, 32, 1, 1, Color(0.2,0.2,0.2)))
;
106     }
107
108
109     // set camera
110     camera_ = new Camera(Vector(0.4, 2, -2), // from
111                          Vector(0, 0, 0) , // to
112                          Vector(0, 1, 0)// up
113                          90)
;              // fov
114
115     // set light sources
116     lightSize_ = 2;
117     light_ = new Light*[lightSize_];
118     light_[0] = new Light( Vector(2, 2.3, -1.5), 0.4, 0.1 );
119     light_[1] = new Light( Vector(-1, 1, -1.5), 0.6, 0.1 );
120
121
122     // INITIAL TEST SCENE, NOT USED ANYMORE !!!
123
124     // main sphere
125 /* AddObject(new Sphere (Vector(0,0,0), 0.4), new Material (0.2, 0.5, 0.7, 32, 0.3, 1, Color(0.8,0.8,0.8))); AddObject(new Sphere (Vector(0,0.1,0), 0.1), new Material (0.2, 0.5, 0.7, 32, 1, 1, Color(0.8,0.8,0.8))); // upper right sphere // AddObject(new Sphere (Vector(0.5,0.5,-0.5), 0.15), // new Material (0.2, 0.6, 0.8, 100, 0.5, 1.3, Color(0.6,0.6,1.0))); // ground AddObject(new Box (Vector(-0.7,-0.2,-0.7), Vector(0.7,-0.15,0.7)), new Material (0.2, 0.5, 0.5, 10, 1, 1, Color(0.5,0.5,0.9))); // upper right box AddObject(new Box (Vector(-0.05,0.3,-0.6), Vector(0.1,0.4,-0.4)), new Material (0.2, 0.5, 0.5, 10, 1, 1, Color(0.5,0.9,0.5))); // upper box AddObject(new Box (Vector(-0.25,0.3,-0.4), Vector(-0.15,0.7,-0.3)), new Material (0.2, 0.3, 0.5, 10, 1, 1, Color(0.5,0.5,0.5))); // back plane AddObject(new Triangle(Vector (-2, -2, 0.7), Vector ( 2, 1.8, 0.7), Vector ( 2, -2, 0.7)), new Material (0.2, 0.3, 0.7, 10, 1, 1, Color(0.5,0.5,0.5))); // bottom plane AddObject(new Box (Vector(-1, -1, -1), Vector(1,-0.9,1)), new Material (0.2, 0.3, 0.5, 10, 1, 1, Color(0.8,0.5,0.5))); AddObject(new Triangle(Vector(-0.8, -0.5, -1), Vector(0.8,-0.5,1), Vector(0.8,-0.5,-1)), new Material (0.2, 0.3, 0.5, 10, 1, 1, Color(0.5,0.9,0.5))); // mid-scene triangles AddObject(new Triangle(Vector (0.4, -0.1, -0.6), Vector (0.6, -0.1, -0.6), Vector (0.6, -0.1, -0.4)), new Material (0.2, 0.3, 0.7, 10, 0.8, 1, Color(0.5,0.9,0.5))); AddObject(new Triangle(Vector (0.5, 0.0, -0.7), Vector (0.7, 0.0, -0.7), Vector (0.7, 0.0, -0.5)), new Material (0.2, 0.3, 0.7, 10, 0.5, 1, Color(0.5,0.9,0.5))); // back plane // AddObject(new Box(Vector(-2, -2, 1.2), Vector(2, 2, 1.3)), // new Material (0.1, 0.2, 0.6, 50, 1, 1, Color(0.9,0.9,0.9))); // sphere on da right side AddObject(new Sphere (Vector(0.6,-0.05,-0.1), 0.05), new Material (0.2, 0.2, 0.8, 100, 1, 1, Color(1,0.5,0.5))); */
126 }
127
128
129 CGRayTracer::~CGRayTracer() {   
130     // clean up
131     for (int i = 0; i < shapeSize_; i++) {
132         delete shape_[i];
133         delete material_[i];
134     }
135     for (int j = 0; j < lightSize_; j++) {
136         delete light_[j];   
137     }
138
139     delete[] shape_;
140     delete[] material_;
141     delete[] light_;
142 }
143
144 void CGRayTracer::AddObject(Shape* shape, Material* material)
145 {
146     shape_[shapeSize_] = shape;
147     material_[shapeSize_] = material;
148
149     shapeSize_++;
150 }
151
152 inline double max(double x, double y) {
153     return (x<y) ? y : x;
154 }
155 inline double min(double x, double y) {
156     return (x>y) ? y : x;
157 }
158
159 Color CGRayTracer::phongIlluminationColor(const Vector& point, const Vector& normal, Material* material) {
160     // calculate Phong illumination
161
162     // code is a slightly modified copy of my Phong homework
163
164     // define intensity variables
165     double dIntensityAmbient  = 0;
166     double dIntensityDiffuse  = 0;
167     double dIntensitySpecular = 0;
168
169     // N = normal
170     const Vector N_norm = normal.normalized();
171     // V = view vector
172     const Vector V = camera_->from - point;
173     const Vector V_norm = V.normalized();
174
175     // process all light sources
176     for (int nLight=0; nLight < lightSize_; nLight++)
177     {
178         // get a single light source
179         const Light* const light = light_[nLight];
180
181         // ALWAYS ambient intensity, even if shadowed
182         dIntensityAmbient  += light->ambient;
183
184         // L = light vector
185         const Vector L = light->pos - point;
186         const Vector L_norm = L.normalized();
187
188         // N*L
189         const double NdotL = dotProduct(N_norm, L_norm);
190
191         // intensity reaching the surface (attenuation)
192         const double dLightDistance = abs(L);
193         double dMaxIntensity = 1.0 / (light->att_constant + dLightDistance*light->att_linear
194                                          + dLightDistance*dLightDistance*light->att_quadric)
;
195         if (dMaxIntensity > 1)
196             dMaxIntensity = 1;
197
198         // get shadow
199         if (shadows_)
200         {
201             // shadow ray must not start at the object's surface !
202             // (else we get in trouble with intersection problems)
203             const Ray shadowray(point+(light->pos - point)*INTERSECTION_OFFSET, light->pos - point);
204             bool shadow = false;
205
206             Shape** ppShape = shape_;
207             for (int shape=0; shape<shapeSize_; shape++)
208             {
209                 // get values of each intersection
210                 double distance;
211                 static Vector _normal, _point;
212
213                 // does the ray intersect the object ?
214                 shadow = shape_[shape]->intersect(shadowray, _point, _normal, distance);
215                 nTests_++;
216
217                 // intersection between light source and surface point ?
218                 if (shadow && distance < dLightDistance)
219                 {
220                     dMaxIntensity *= 1-material_[shape]->opacity;
221                     if (dMaxIntensity > 0.01)
222                         shadow = false;
223                     else
224                         // 100% shadow
225                         break;
226                 }
227             }
228             // shadowed ! no diffuse or specular light possible
229             if (shadow)
230                 continue;
231         }
232
233         // R*V = (2*N*(N*L)-L)*V
234         const double RdotV = max(dotProduct((2*NdotL*N_norm - L_norm).normalized(), V_norm), 0);
235
236         // diffuse intensity
237         dIntensityDiffuse  += dMaxIntensity * material->kd * NdotL;
238         // specular intensity
239         dIntensitySpecular += dMaxIntensity * material->ks * pow(RdotV, material->n);
240     }
241
242     // return clamped color
243     return material->color * min(dIntensityAmbient+dIntensityDiffuse+dIntensitySpecular,1);
244 }
245
246 Color CGRayTracer::rayTrace(const Ray& r, int nDepth) {
247     // Check each shape for intersektion.
248     // Return phong color of nearest shape
249     // that is hit.
250
251     if (nDepth++ > maxDepth_)
252         return clrBackground;
253
254     // shape that we found
255     int closestShape = -1;
256     // its parameters
257     double closestDistance = 99999999;
258     Vector closestPoint;
259     Vector closestNormal;
260
261     Shape** ppShape = shape_;
262     for (int shape=0; shape<shapeSize_; shape++)
263     {
264         // get values of each intersection
265         double distance;
266         static Vector normal;
267         static Vector point;
268
269         // internal counter
270         nTests_++;
271
272         // does the ray intersect the object ?
273         if (shape_[shape]->intersect(r, point, normal, distance))
274             // closer than any intersection we tested before ?
275             if (distance < closestDistance)
276             {
277                 closestDistance = distance;
278                 closestShape    = shape;
279                 closestPoint    = point;
280                 closestNormal   = normal;
281             }
282     }
283
284     // no intersection found
285     if (closestShape < 0)
286         return clrBackground;
287     Material& material = *(material_[closestShape]);
288
289    
290     // object itself
291     Color color = phongIlluminationColor(closestPoint, closestNormal, &material);
292
293     // reflection
294     if (material.ks >= 0.001)
295     {
296         // get reflected ray
297         double reflected_angle     = -dotProduct(closestNormal, r.getDirection());
298         if (reflected_angle < 0)
299         {
300             closestNormal   *= -1;
301             reflected_angle *= -1;
302         }
303
304         const Vector reflected_direction = r.getDirection() + 2*reflected_angle*closestNormal;
305
306         // reflected ray must not start at the object's surface !
307         // (else we get in trouble with intersection problems)
308         const Ray reflected_ray(closestPoint+closestNormal*INTERSECTION_OFFSET,
309                                 reflected_direction, material.lightspeed)
;
310
311         // get color
312         const Color reflected_color = rayTrace(reflected_ray, nDepth) * material.ks * material.opacity;
313
314         // add reflective part
315         color += reflected_color;
316     }
317
318     // refraction
319     if (material.opacity < 1)
320     {
321         double lightspeedratio = r.getLightspeed() / material.lightspeed;
322
323         double refracted_angle     = -dotProduct(closestNormal, r.getDirection());
324         if (refracted_angle < 0)
325         {
326             closestNormal   *= -1;
327             lightspeedratio = 1/lightspeedratio;
328         }
329
330         // compute refraction vector (its direction)
331         const Vector L       = -r.getDirection();
332         const double NdotL   = dotProduct(closestNormal, L);
333         // Snell's Law (splitted up for better readibility)
334         const double Root    = 1 - lightspeedratio*lightspeedratio*(1 - NdotL*NdotL);
335         const double Bracket = lightspeedratio*NdotL - sqrt(Root);
336         const Vector T       = Bracket*closestNormal - lightspeedratio*L;
337
338         // slight shift of new ray origin
339         Vector T_origin      = closestPoint;
340         if (NdotL < 0)
341             T_origin += closestNormal*10*INTERSECTION_OFFSET;
342         else
343             T_origin -= closestNormal*10*INTERSECTION_OFFSET;
344
345         const Ray refracted_ray(T_origin, T, material.lightspeed);
346                                 //r.getDirection(), material.lightspeed);
347        
348         // get color
349         const Color refracted_color = rayTrace(refracted_ray, nDepth) * (1-material.opacity);
350
351         // add reflective part
352         color *= material.opacity;
353         color += refracted_color;
354     }
355
356     // clamp color
357     color[0] = min(color[0], 1);
358     color[1] = min(color[1], 1);
359     color[2] = min(color[2], 1);
360 // color[0] = max(color[0], 0);
361 // color[1] = max(color[1], 0);
362 // color[2] = max(color[2], 0);
363
364     return color;
365 }
366
367 void CGRayTracer::writePPMPixel(const Color& c) {
368     // write pixel to file
369     (*out_) << ((unsigned char)(c[0]*255))
370             << ((unsigned char)(c[1]*255))
371             << ((unsigned char)(c[2]*255));
372 }
373    
374 void CGRayTracer::writeImage(char* fileName) {
375
376     cout << "saving " << fileName << " - ";
377     // create output stream
378     out_ = new ofstream(fileName);
379
380     // write PPM header
381 #ifdef _MSC_VER
382     (*out_) << binary;
383 #endif
384     (*out_) << "P6" << endl
385             << width_ << " " << height_ << endl
386             << 255 << endl;
387    
388     // write all pixel
389     for(unsigned int i=0; i<imagey_; i++)
390         for(unsigned int j=0; j<imagex_; j++)
391             writePPMPixel(image_[i*imagex_+j]);
392
393     // delete output stream
394     delete out_;
395
396     cout << "done." << endl;
397 }
398
399
400 void CGRayTracer::createImage(int width, int height) {
401     // convert field-of-view angle to rad
402     const double fovy_rad = camera_->fovy * PI/180;
403
404     // look vector
405     const Vector Look  = camera_->to - camera_->from;
406
407     // delta angles
408     const double up_angle_delta    = fovy_rad / height;
409     const double right_angle_delta = fovy_rad / height;//width;
410
411     // normalized vectors that describe the view plane (right handed !)
412     const Vector Right = camera_->up.normalized() * Look.normalized();
413     const Vector Up    = Right * Look.normalized();
414
415     // image
416     const double widthoffset  = imagex_/(double)width;
417     const double heightoffset = imagey_/(double)height;
418     const double pixelsize = detail_;
419
420     for(int i=0; i<height; i++)
421     {
422         for(int j=0; j<width; j++)
423         {
424             // create rays for each pixel and send through scene
425
426             // skip if already calculated
427             const int truex = int(j*widthoffset);
428             const int truey = int(i*heightoffset);
429             Color& clrPixel = image_[truey*imagex_+truex];
430             if (clrPixel[0] >= 0)
431             {
432                 // draw already calculated pixels
433 #ifndef DONT_DRAW
434                 glColor3dv(clrPixel.rep());
435                 glRectd(truex, height_-truey,
436                         truex+pixelsize, height_-(truey+pixelsize))
;
437 #endif
438                 continue;
439             }
440
441             // angles
442             const double up_angle    = (i-height/2.0) * up_angle_delta;
443             const double right_angle = (j-width /2.0) * right_angle_delta;
444
445             // correspending vectors
446             const Vector CurrentUp    = Up    * abs(Look) * tan(up_angle);
447             const Vector CurrentRight = Right * abs(Look) * tan(right_angle);
448
449             // put it all together
450             const Vector LookAt = Look + CurrentUp + CurrentRight;
451
452             // ray tracing
453             clrPixel = rayTrace(Ray(camera_->from, LookAt));
454
455             // draw pixel
456 #ifndef DONT_DRAW
457             glColor3dv(clrPixel.rep());
458             glRectd(truex, height_-truey,
459                     truex+pixelsize, height_-(truey+pixelsize))
;
460 #endif
461         }
462     }
463 }
464
465
466 void CGRayTracer::onInit() {
467     // no perspective drawing, just pure 2D display
468     glMatrixMode(GL_PROJECTION);
469     glLoadIdentity();
470     gluOrtho2D(0, imagex_-1, 0, imagey_-1);
471
472     glClearColor(clrBackground[0], clrBackground[1], clrBackground[2],1);
473     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
474
475     // start timer
476     start_ = clock();
477 }
478
479 void CGRayTracer::onSize(unsigned int newWidth,unsigned int newHeight) {         
480     width_ = newWidth;
481     height_ = newHeight; 
482    
483     glViewport(0,0, width_-1, height_-1);
484     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
485     onDraw();
486 }
487
488 void CGRayTracer::onKey(unsigned char key) {
489     switch (key) {
490     case 27: { exit(0); break; }                       
491     case 'a': antialias_ = !antialias_; onDraw(); break;
492     }
493 }
494
495 void CGRayTracer::onIdle() {
496     if (detail_ > 1)
497     {
498         // next detail level
499         detail_ /= 2;
500         createImage(imagex_ / detail_, imagey_ / detail_);
501
502         if (detail_ > 1)
503         {
504             // detail level completed
505             cout << detail_ << "x" << detail_ << " level done" << endl;
506         }
507         else
508         {
509             // stop timer
510             clock_t finish = clock();
511             cout << "... image completed !" << endl;
512
513             writeImage("raytracer.ppm");
514            
515             double seconds = (finish - start_)/(double)CLOCKS_PER_SEC;
516             // show some statistics
517             cout << endl
518                  << imagey_ << "x" << imagex_ << " pixels rendered using " << nTests_ << " intersection tests in "
519                  << seconds << " seconds" << endl
520                  << "=> " << int(imagex_*imagey_/seconds) << " pixels/sec and "
521                  << nTests_/double(imagex_*imagey_) << " tests/pixel (ray depth=" << maxDepth_ << ")" << endl;
522
523         }
524     }
525 }
526
527 void CGRayTracer::onDraw() {
528 // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
529
530     // "pixel" size
531     const double size = detail_;
532
533     if (!antialias_ || detail_ > 1)
534         // process all pixels
535         for (unsigned int i=0; i<imagey_; i++)
536             for (unsigned int j=0; j<imagex_; j++)
537             {
538                 // draw only if already calculated
539                 const Color& color = image_[i*imagex_+j];
540                 if (color[0] >= 0)
541                 {
542                     glColor3dv(color.rep());
543                     glRectd(j, imagey_-i, j+size, imagey_-(i+size));
544                 }
545             }
546     else
547         // process all pixels
548         for (unsigned int i=0; i<imagey_; i++)
549             for (unsigned int j=0; j<imagex_; j++)
550             {
551                 if (i==0 || j==0)
552                 {
553                     glColor3dv(image_[i*imagex_+j].rep());
554                     glRectd(j, imagey_-i, j+size, imagey_-(i+size));
555                 }
556                 else
557                 {
558                     // weight neighbours
559                     const Color color = (image_[(i-1)*imagex_+j] +
560                                          image_[(i-1)*imagex_+j-1] +
561                                          image_[(i-1)*imagex_+j+1] +
562                                        4*image_[ i   *imagex_+j] +
563                                          image_[ i   *imagex_+j-1] +
564                                          image_[ i   *imagex_+j+1] +
565                                          image_[(i+1)*imagex_+j] +
566                                          image_[(i+1)*imagex_+j-1] +
567                                          image_[(i+1)*imagex_+j+1])
/ 12;
568                     glColor3dv(color.rep());
569                     glRectd(j, imagey_-i, j+size, imagey_-(i+size));
570                 }
571             }
572 }
573
574
575 // Hauptprogramm
576 int main(int argc, char* argv[]) {
577
578     unsigned int maxx;
579     unsigned int maxy;
580
581     cout << "Bildformat bitte eingeben !" << endl
582          << "Breite: ";
583     cin  >> maxx;
584     cout << "Hoehe : ";
585     cin  >> maxy;
586    
587     // Erzeuge eine Instanz der Beispiel-Anwendung:
588     CGRayTracer sample(maxx, maxy);
589
590     cout << endl
591          << "Tastenbelegung:" << endl
592          << "ESC Programm beenden" << endl
593 // << "a Antialiasing ein/aus" << endl
594          << endl
595          << "Das Bild wird automatisch unter \"raytracer.ppm\" gespeichert." << endl << endl;
596
597     // Starte die Beispiel-Anwendung:
598    
599     sample.start("Stephan Brumme, 702544", false, maxx, maxy);
600
601     return 0;
602 }
603