Home | History | Annotate | Download | only in v8-v4
      1 // The ray tracer code in this file is written by Adam Burmister. It
      2 // is available in its original form from:
      3 //
      4 //   http://labs.flog.nz.co/raytracer/
      5 //
      6 // It has been modified slightly by Google to work as a standalone
      7 // benchmark, but the all the computational code remains
      8 // untouched. This file also contains a copy of parts of the Prototype
      9 // JavaScript framework which is used by the ray tracer.
     10 
     11 // Variable used to hold a number that can be used to verify that
     12 // the scene was ray traced correctly.
     13 var checkNumber;
     14 
     15 
     16 // ------------------------------------------------------------------------
     17 // ------------------------------------------------------------------------
     18 
     19 // The following is a copy of parts of the Prototype JavaScript library:
     20 
     21 // Prototype JavaScript framework, version 1.5.0
     22 // (c) 2005-2007 Sam Stephenson
     23 //
     24 // Prototype is freely distributable under the terms of an MIT-style license.
     25 // For details, see the Prototype web site: http://prototype.conio.net/
     26 
     27 
     28 var Class = {
     29   create: function() {
     30     return function() {
     31       this.initialize.apply(this, arguments);
     32     }
     33   }
     34 };
     35 
     36 
     37 Object.extend = function(destination, source) {
     38   for (var property in source) {
     39     destination[property] = source[property];
     40   }
     41   return destination;
     42 };
     43 
     44 
     45 // ------------------------------------------------------------------------
     46 // ------------------------------------------------------------------------
     47 
     48 // The rest of this file is the actual ray tracer written by Adam
     49 // Burmister. It's a concatenation of the following files:
     50 //
     51 //   flog/color.js
     52 //   flog/light.js
     53 //   flog/vector.js
     54 //   flog/ray.js
     55 //   flog/scene.js
     56 //   flog/material/basematerial.js
     57 //   flog/material/solid.js
     58 //   flog/material/chessboard.js
     59 //   flog/shape/baseshape.js
     60 //   flog/shape/sphere.js
     61 //   flog/shape/plane.js
     62 //   flog/intersectioninfo.js
     63 //   flog/camera.js
     64 //   flog/background.js
     65 //   flog/engine.js
     66 
     67 
     68 /* Fake a Flog.* namespace */
     69 if(typeof(Flog) == 'undefined') var Flog = {};
     70 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
     71 
     72 Flog.RayTracer.Color = Class.create();
     73 
     74 Flog.RayTracer.Color.prototype = {
     75     red : 0.0,
     76     green : 0.0,
     77     blue : 0.0,
     78 
     79     initialize : function(r, g, b) {
     80         if(!r) r = 0.0;
     81         if(!g) g = 0.0;
     82         if(!b) b = 0.0;
     83 
     84         this.red = r;
     85         this.green = g;
     86         this.blue = b;
     87     },
     88 
     89     add : function(c1, c2){
     90         var result = new Flog.RayTracer.Color(0,0,0);
     91 
     92         result.red = c1.red + c2.red;
     93         result.green = c1.green + c2.green;
     94         result.blue = c1.blue + c2.blue;
     95 
     96         return result;
     97     },
     98 
     99     addScalar: function(c1, s){
    100         var result = new Flog.RayTracer.Color(0,0,0);
    101 
    102         result.red = c1.red + s;
    103         result.green = c1.green + s;
    104         result.blue = c1.blue + s;
    105 
    106         result.limit();
    107 
    108         return result;
    109     },
    110 
    111     subtract: function(c1, c2){
    112         var result = new Flog.RayTracer.Color(0,0,0);
    113 
    114         result.red = c1.red - c2.red;
    115         result.green = c1.green - c2.green;
    116         result.blue = c1.blue - c2.blue;
    117 
    118         return result;
    119     },
    120 
    121     multiply : function(c1, c2) {
    122         var result = new Flog.RayTracer.Color(0,0,0);
    123 
    124         result.red = c1.red * c2.red;
    125         result.green = c1.green * c2.green;
    126         result.blue = c1.blue * c2.blue;
    127 
    128         return result;
    129     },
    130 
    131     multiplyScalar : function(c1, f) {
    132         var result = new Flog.RayTracer.Color(0,0,0);
    133 
    134         result.red = c1.red * f;
    135         result.green = c1.green * f;
    136         result.blue = c1.blue * f;
    137 
    138         return result;
    139     },
    140 
    141     divideFactor : function(c1, f) {
    142         var result = new Flog.RayTracer.Color(0,0,0);
    143 
    144         result.red = c1.red / f;
    145         result.green = c1.green / f;
    146         result.blue = c1.blue / f;
    147 
    148         return result;
    149     },
    150 
    151     limit: function(){
    152         this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
    153         this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
    154         this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
    155     },
    156 
    157     distance : function(color) {
    158         var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
    159         return d;
    160     },
    161 
    162     blend: function(c1, c2, w){
    163         var result = new Flog.RayTracer.Color(0,0,0);
    164         result = Flog.RayTracer.Color.prototype.add(
    165                     Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
    166                     Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
    167                   );
    168         return result;
    169     },
    170 
    171     brightness : function() {
    172         var r = Math.floor(this.red*255);
    173         var g = Math.floor(this.green*255);
    174         var b = Math.floor(this.blue*255);
    175         return (r * 77 + g * 150 + b * 29) >> 8;
    176     },
    177 
    178     toString : function () {
    179         var r = Math.floor(this.red*255);
    180         var g = Math.floor(this.green*255);
    181         var b = Math.floor(this.blue*255);
    182 
    183         return "rgb("+ r +","+ g +","+ b +")";
    184     }
    185 }
    186 /* Fake a Flog.* namespace */
    187 if(typeof(Flog) == 'undefined') var Flog = {};
    188 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    189 
    190 Flog.RayTracer.Light = Class.create();
    191 
    192 Flog.RayTracer.Light.prototype = {
    193     position: null,
    194     color: null,
    195     intensity: 10.0,
    196 
    197     initialize : function(pos, color, intensity) {
    198         this.position = pos;
    199         this.color = color;
    200         this.intensity = (intensity ? intensity : 10.0);
    201     },
    202 
    203     getIntensity: function(distance){
    204         if(distance >= intensity) return 0;
    205 
    206         return Math.pow((intensity - distance) / strength, 0.2);
    207     },
    208 
    209     toString : function () {
    210         return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
    211     }
    212 }
    213 /* Fake a Flog.* namespace */
    214 if(typeof(Flog) == 'undefined') var Flog = {};
    215 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    216 
    217 Flog.RayTracer.Vector = Class.create();
    218 
    219 Flog.RayTracer.Vector.prototype = {
    220     x : 0.0,
    221     y : 0.0,
    222     z : 0.0,
    223 
    224     initialize : function(x, y, z) {
    225         this.x = (x ? x : 0);
    226         this.y = (y ? y : 0);
    227         this.z = (z ? z : 0);
    228     },
    229 
    230     copy: function(vector){
    231         this.x = vector.x;
    232         this.y = vector.y;
    233         this.z = vector.z;
    234     },
    235 
    236     normalize : function() {
    237         var m = this.magnitude();
    238         return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
    239     },
    240 
    241     magnitude : function() {
    242         return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
    243     },
    244 
    245     cross : function(w) {
    246         return new Flog.RayTracer.Vector(
    247                                             -this.z * w.y + this.y * w.z,
    248                                            this.z * w.x - this.x * w.z,
    249                                           -this.y * w.x + this.x * w.y);
    250     },
    251 
    252     dot : function(w) {
    253         return this.x * w.x + this.y * w.y + this.z * w.z;
    254     },
    255 
    256     add : function(v, w) {
    257         return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
    258     },
    259 
    260     subtract : function(v, w) {
    261         if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
    262         return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
    263     },
    264 
    265     multiplyVector : function(v, w) {
    266         return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
    267     },
    268 
    269     multiplyScalar : function(v, w) {
    270         return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
    271     },
    272 
    273     toString : function () {
    274         return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
    275     }
    276 }
    277 /* Fake a Flog.* namespace */
    278 if(typeof(Flog) == 'undefined') var Flog = {};
    279 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    280 
    281 Flog.RayTracer.Ray = Class.create();
    282 
    283 Flog.RayTracer.Ray.prototype = {
    284     position : null,
    285     direction : null,
    286     initialize : function(pos, dir) {
    287         this.position = pos;
    288         this.direction = dir;
    289     },
    290 
    291     toString : function () {
    292         return 'Ray [' + this.position + ',' + this.direction + ']';
    293     }
    294 }
    295 /* Fake a Flog.* namespace */
    296 if(typeof(Flog) == 'undefined') var Flog = {};
    297 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    298 
    299 Flog.RayTracer.Scene = Class.create();
    300 
    301 Flog.RayTracer.Scene.prototype = {
    302     camera : null,
    303     shapes : [],
    304     lights : [],
    305     background : null,
    306 
    307     initialize : function() {
    308         this.camera = new Flog.RayTracer.Camera(
    309             new Flog.RayTracer.Vector(0,0,-5),
    310             new Flog.RayTracer.Vector(0,0,1),
    311             new Flog.RayTracer.Vector(0,1,0)
    312         );
    313         this.shapes = new Array();
    314         this.lights = new Array();
    315         this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
    316     }
    317 }
    318 /* Fake a Flog.* namespace */
    319 if(typeof(Flog) == 'undefined') var Flog = {};
    320 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    321 if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
    322 
    323 Flog.RayTracer.Material.BaseMaterial = Class.create();
    324 
    325 Flog.RayTracer.Material.BaseMaterial.prototype = {
    326 
    327     gloss: 2.0,             // [0...infinity] 0 = matt
    328     transparency: 0.0,      // 0=opaque
    329     reflection: 0.0,        // [0...infinity] 0 = no reflection
    330     refraction: 0.50,
    331     hasTexture: false,
    332 
    333     initialize : function() {
    334 
    335     },
    336 
    337     getColor: function(u, v){
    338 
    339     },
    340 
    341     wrapUp: function(t){
    342         t = t % 2.0;
    343         if(t < -1) t += 2.0;
    344         if(t >= 1) t -= 2.0;
    345         return t;
    346     },
    347 
    348     toString : function () {
    349         return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
    350     }
    351 }
    352 /* Fake a Flog.* namespace */
    353 if(typeof(Flog) == 'undefined') var Flog = {};
    354 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    355 
    356 Flog.RayTracer.Material.Solid = Class.create();
    357 
    358 Flog.RayTracer.Material.Solid.prototype = Object.extend(
    359     new Flog.RayTracer.Material.BaseMaterial(), {
    360         initialize : function(color, reflection, refraction, transparency, gloss) {
    361             this.color = color;
    362             this.reflection = reflection;
    363             this.transparency = transparency;
    364             this.gloss = gloss;
    365             this.hasTexture = false;
    366         },
    367 
    368         getColor: function(u, v){
    369             return this.color;
    370         },
    371 
    372         toString : function () {
    373             return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
    374         }
    375     }
    376 );
    377 /* Fake a Flog.* namespace */
    378 if(typeof(Flog) == 'undefined') var Flog = {};
    379 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    380 
    381 Flog.RayTracer.Material.Chessboard = Class.create();
    382 
    383 Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
    384     new Flog.RayTracer.Material.BaseMaterial(), {
    385         colorEven: null,
    386         colorOdd: null,
    387         density: 0.5,
    388 
    389         initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
    390             this.colorEven = colorEven;
    391             this.colorOdd = colorOdd;
    392             this.reflection = reflection;
    393             this.transparency = transparency;
    394             this.gloss = gloss;
    395             this.density = density;
    396             this.hasTexture = true;
    397         },
    398 
    399         getColor: function(u, v){
    400             var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
    401 
    402             if(t < 0.0)
    403                 return this.colorEven;
    404             else
    405                 return this.colorOdd;
    406         },
    407 
    408         toString : function () {
    409             return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
    410         }
    411     }
    412 );
    413 /* Fake a Flog.* namespace */
    414 if(typeof(Flog) == 'undefined') var Flog = {};
    415 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    416 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
    417 
    418 Flog.RayTracer.Shape.BaseShape = Class.create();
    419 
    420 Flog.RayTracer.Shape.BaseShape.prototype = {
    421     position: null,
    422     material: null,
    423 
    424     initialize : function() {
    425         this.position = new Vector(0,0,0);
    426         this.material = new Flog.RayTracer.Material.SolidMaterial(
    427             new Flog.RayTracer.Color(1,0,1),
    428             0,
    429             0,
    430             0
    431         );
    432     },
    433 
    434     toString : function () {
    435         return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
    436     }
    437 }
    438 /* Fake a Flog.* namespace */
    439 if(typeof(Flog) == 'undefined') var Flog = {};
    440 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    441 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
    442 
    443 Flog.RayTracer.Shape.Sphere = Class.create();
    444 
    445 Flog.RayTracer.Shape.Sphere.prototype = {
    446     initialize : function(pos, radius, material) {
    447         this.radius = radius;
    448         this.position = pos;
    449         this.material = material;
    450     },
    451 
    452     intersect: function(ray){
    453         var info = new Flog.RayTracer.IntersectionInfo();
    454         info.shape = this;
    455 
    456         var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
    457 
    458         var B = dst.dot(ray.direction);
    459         var C = dst.dot(dst) - (this.radius * this.radius);
    460         var D = (B * B) - C;
    461 
    462         if(D > 0){ // intersection!
    463             info.isHit = true;
    464             info.distance = (-B) - Math.sqrt(D);
    465             info.position = Flog.RayTracer.Vector.prototype.add(
    466                                                 ray.position,
    467                                                 Flog.RayTracer.Vector.prototype.multiplyScalar(
    468                                                     ray.direction,
    469                                                     info.distance
    470                                                 )
    471                                             );
    472             info.normal = Flog.RayTracer.Vector.prototype.subtract(
    473                                             info.position,
    474                                             this.position
    475                                         ).normalize();
    476 
    477             info.color = this.material.getColor(0,0);
    478         } else {
    479             info.isHit = false;
    480         }
    481         return info;
    482     },
    483 
    484     toString : function () {
    485         return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
    486     }
    487 }
    488 /* Fake a Flog.* namespace */
    489 if(typeof(Flog) == 'undefined') var Flog = {};
    490 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    491 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
    492 
    493 Flog.RayTracer.Shape.Plane = Class.create();
    494 
    495 Flog.RayTracer.Shape.Plane.prototype = {
    496     d: 0.0,
    497 
    498     initialize : function(pos, d, material) {
    499         this.position = pos;
    500         this.d = d;
    501         this.material = material;
    502     },
    503 
    504     intersect: function(ray){
    505         var info = new Flog.RayTracer.IntersectionInfo();
    506 
    507         var Vd = this.position.dot(ray.direction);
    508         if(Vd == 0) return info; // no intersection
    509 
    510         var t = -(this.position.dot(ray.position) + this.d) / Vd;
    511         if(t <= 0) return info;
    512 
    513         info.shape = this;
    514         info.isHit = true;
    515         info.position = Flog.RayTracer.Vector.prototype.add(
    516                                             ray.position,
    517                                             Flog.RayTracer.Vector.prototype.multiplyScalar(
    518                                                 ray.direction,
    519                                                 t
    520                                             )
    521                                         );
    522         info.normal = this.position;
    523         info.distance = t;
    524 
    525         if(this.material.hasTexture){
    526             var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
    527             var vV = vU.cross(this.position);
    528             var u = info.position.dot(vU);
    529             var v = info.position.dot(vV);
    530             info.color = this.material.getColor(u,v);
    531         } else {
    532             info.color = this.material.getColor(0,0);
    533         }
    534 
    535         return info;
    536     },
    537 
    538     toString : function () {
    539         return 'Plane [' + this.position + ', d=' + this.d + ']';
    540     }
    541 }
    542 /* Fake a Flog.* namespace */
    543 if(typeof(Flog) == 'undefined') var Flog = {};
    544 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    545 
    546 Flog.RayTracer.IntersectionInfo = Class.create();
    547 
    548 Flog.RayTracer.IntersectionInfo.prototype = {
    549     isHit: false,
    550     hitCount: 0,
    551     shape: null,
    552     position: null,
    553     normal: null,
    554     color: null,
    555     distance: null,
    556 
    557     initialize : function() {
    558         this.color = new Flog.RayTracer.Color(0,0,0);
    559     },
    560 
    561     toString : function () {
    562         return 'Intersection [' + this.position + ']';
    563     }
    564 }
    565 /* Fake a Flog.* namespace */
    566 if(typeof(Flog) == 'undefined') var Flog = {};
    567 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    568 
    569 Flog.RayTracer.Camera = Class.create();
    570 
    571 Flog.RayTracer.Camera.prototype = {
    572     position: null,
    573     lookAt: null,
    574     equator: null,
    575     up: null,
    576     screen: null,
    577 
    578     initialize : function(pos, lookAt, up) {
    579         this.position = pos;
    580         this.lookAt = lookAt;
    581         this.up = up;
    582         this.equator = lookAt.normalize().cross(this.up);
    583         this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
    584     },
    585 
    586     getRay: function(vx, vy){
    587         var pos = Flog.RayTracer.Vector.prototype.subtract(
    588             this.screen,
    589             Flog.RayTracer.Vector.prototype.subtract(
    590                 Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
    591                 Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
    592             )
    593         );
    594         pos.y = pos.y * -1;
    595         var dir = Flog.RayTracer.Vector.prototype.subtract(
    596             pos,
    597             this.position
    598         );
    599 
    600         var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
    601 
    602         return ray;
    603     },
    604 
    605     toString : function () {
    606         return 'Ray []';
    607     }
    608 }
    609 /* Fake a Flog.* namespace */
    610 if(typeof(Flog) == 'undefined') var Flog = {};
    611 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    612 
    613 Flog.RayTracer.Background = Class.create();
    614 
    615 Flog.RayTracer.Background.prototype = {
    616     color : null,
    617     ambience : 0.0,
    618 
    619     initialize : function(color, ambience) {
    620         this.color = color;
    621         this.ambience = ambience;
    622     }
    623 }
    624 /* Fake a Flog.* namespace */
    625 if(typeof(Flog) == 'undefined') var Flog = {};
    626 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
    627 
    628 Flog.RayTracer.Engine = Class.create();
    629 
    630 Flog.RayTracer.Engine.prototype = {
    631     canvas: null, /* 2d context we can render to */
    632 
    633     initialize: function(options){
    634         this.options = Object.extend({
    635                 canvasHeight: 100,
    636                 canvasWidth: 100,
    637                 pixelWidth: 2,
    638                 pixelHeight: 2,
    639                 renderDiffuse: false,
    640                 renderShadows: false,
    641                 renderHighlights: false,
    642                 renderReflections: false,
    643                 rayDepth: 2
    644             }, options || {});
    645 
    646         this.options.canvasHeight /= this.options.pixelHeight;
    647         this.options.canvasWidth /= this.options.pixelWidth;
    648 
    649         /* TODO: dynamically include other scripts */
    650     },
    651 
    652     setPixel: function(x, y, color){
    653         var pxW, pxH;
    654         pxW = this.options.pixelWidth;
    655         pxH = this.options.pixelHeight;
    656 
    657         if (this.canvas) {
    658           this.canvas.fillStyle = color.toString();
    659           this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
    660         } else {
    661           if (x ===  y) {
    662             checkNumber += color.brightness();
    663           }
    664           // print(x * pxW, y * pxH, pxW, pxH);
    665         }
    666     },
    667 
    668     renderScene: function(scene, canvas){
    669         checkNumber = 0;
    670         /* Get canvas */
    671         if (canvas) {
    672           this.canvas = canvas.getContext("2d");
    673         } else {
    674           this.canvas = null;
    675         }
    676 
    677         var canvasHeight = this.options.canvasHeight;
    678         var canvasWidth = this.options.canvasWidth;
    679 
    680         for(var y=0; y < canvasHeight; y++){
    681             for(var x=0; x < canvasWidth; x++){
    682                 var yp = y * 1.0 / canvasHeight * 2 - 1;
    683           		var xp = x * 1.0 / canvasWidth * 2 - 1;
    684 
    685           		var ray = scene.camera.getRay(xp, yp);
    686 
    687           		var color = this.getPixelColor(ray, scene);
    688 
    689             	this.setPixel(x, y, color);
    690             }
    691         }
    692         if (checkNumber !== 2321) {
    693           throw new Error("Scene rendered incorrectly");
    694         }
    695     },
    696 
    697     getPixelColor: function(ray, scene){
    698         var info = this.testIntersection(ray, scene, null);
    699         if(info.isHit){
    700             var color = this.rayTrace(info, ray, scene, 0);
    701             return color;
    702         }
    703         return scene.background.color;
    704     },
    705 
    706     testIntersection: function(ray, scene, exclude){
    707         var hits = 0;
    708         var best = new Flog.RayTracer.IntersectionInfo();
    709         best.distance = 2000;
    710 
    711         for(var i=0; i<scene.shapes.length; i++){
    712             var shape = scene.shapes[i];
    713 
    714             if(shape != exclude){
    715                 var info = shape.intersect(ray);
    716                 if(info.isHit && info.distance >= 0 && info.distance < best.distance){
    717                     best = info;
    718                     hits++;
    719                 }
    720             }
    721         }
    722         best.hitCount = hits;
    723         return best;
    724     },
    725 
    726     getReflectionRay: function(P,N,V){
    727         var c1 = -N.dot(V);
    728         var R1 = Flog.RayTracer.Vector.prototype.add(
    729             Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
    730             V
    731         );
    732         return new Flog.RayTracer.Ray(P, R1);
    733     },
    734 
    735     rayTrace: function(info, ray, scene, depth){
    736         // Calc ambient
    737         var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
    738         var oldColor = color;
    739         var shininess = Math.pow(10, info.shape.material.gloss + 1);
    740 
    741         for(var i=0; i<scene.lights.length; i++){
    742             var light = scene.lights[i];
    743 
    744             // Calc diffuse lighting
    745             var v = Flog.RayTracer.Vector.prototype.subtract(
    746                                 light.position,
    747                                 info.position
    748                             ).normalize();
    749 
    750             if(this.options.renderDiffuse){
    751                 var L = v.dot(info.normal);
    752                 if(L > 0.0){
    753                     color = Flog.RayTracer.Color.prototype.add(
    754                                         color,
    755                                         Flog.RayTracer.Color.prototype.multiply(
    756                                             info.color,
    757                                             Flog.RayTracer.Color.prototype.multiplyScalar(
    758                                                 light.color,
    759                                                 L
    760                                             )
    761                                         )
    762                                     );
    763                 }
    764             }
    765 
    766             // The greater the depth the more accurate the colours, but
    767             // this is exponentially (!) expensive
    768             if(depth <= this.options.rayDepth){
    769           // calculate reflection ray
    770           if(this.options.renderReflections && info.shape.material.reflection > 0)
    771           {
    772               var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
    773               var refl = this.testIntersection(reflectionRay, scene, info.shape);
    774 
    775               if (refl.isHit && refl.distance > 0){
    776                   refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
    777               } else {
    778                   refl.color = scene.background.color;
    779                         }
    780 
    781                   color = Flog.RayTracer.Color.prototype.blend(
    782                     color,
    783                     refl.color,
    784                     info.shape.material.reflection
    785                   );
    786           }
    787 
    788                 // Refraction
    789                 /* TODO */
    790             }
    791 
    792             /* Render shadows and highlights */
    793 
    794             var shadowInfo = new Flog.RayTracer.IntersectionInfo();
    795 
    796             if(this.options.renderShadows){
    797                 var shadowRay = new Flog.RayTracer.Ray(info.position, v);
    798 
    799                 shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
    800                 if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
    801                     var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
    802                     var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
    803                     color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
    804                 }
    805             }
    806 
    807       // Phong specular highlights
    808       if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
    809         var Lv = Flog.RayTracer.Vector.prototype.subtract(
    810                             info.shape.position,
    811                             light.position
    812                         ).normalize();
    813 
    814         var E = Flog.RayTracer.Vector.prototype.subtract(
    815                             scene.camera.position,
    816                             info.shape.position
    817                         ).normalize();
    818 
    819         var H = Flog.RayTracer.Vector.prototype.subtract(
    820                             E,
    821                             Lv
    822                         ).normalize();
    823 
    824         var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
    825         color = Flog.RayTracer.Color.prototype.add(
    826                             Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
    827                             color
    828                         );
    829       }
    830         }
    831         color.limit();
    832         return color;
    833     }
    834 };
    835 
    836 
    837 function renderScene(){
    838     var scene = new Flog.RayTracer.Scene();
    839 
    840     scene.camera = new Flog.RayTracer.Camera(
    841                         new Flog.RayTracer.Vector(0, 0, -15),
    842                         new Flog.RayTracer.Vector(-0.2, 0, 5),
    843                         new Flog.RayTracer.Vector(0, 1, 0)
    844                     );
    845 
    846     scene.background = new Flog.RayTracer.Background(
    847                                 new Flog.RayTracer.Color(0.5, 0.5, 0.5),
    848                                 0.4
    849                             );
    850 
    851     var sphere = new Flog.RayTracer.Shape.Sphere(
    852         new Flog.RayTracer.Vector(-1.5, 1.5, 2),
    853         1.5,
    854         new Flog.RayTracer.Material.Solid(
    855             new Flog.RayTracer.Color(0,0.5,0.5),
    856             0.3,
    857             0.0,
    858             0.0,
    859             2.0
    860         )
    861     );
    862 
    863     var sphere1 = new Flog.RayTracer.Shape.Sphere(
    864         new Flog.RayTracer.Vector(1, 0.25, 1),
    865         0.5,
    866         new Flog.RayTracer.Material.Solid(
    867             new Flog.RayTracer.Color(0.9,0.9,0.9),
    868             0.1,
    869             0.0,
    870             0.0,
    871             1.5
    872         )
    873     );
    874 
    875     var plane = new Flog.RayTracer.Shape.Plane(
    876                                 new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
    877                                 1.2,
    878                                 new Flog.RayTracer.Material.Chessboard(
    879                                     new Flog.RayTracer.Color(1,1,1),
    880                                     new Flog.RayTracer.Color(0,0,0),
    881                                     0.2,
    882                                     0.0,
    883                                     1.0,
    884                                     0.7
    885                                 )
    886                             );
    887 
    888     scene.shapes.push(plane);
    889     scene.shapes.push(sphere);
    890     scene.shapes.push(sphere1);
    891 
    892     var light = new Flog.RayTracer.Light(
    893         new Flog.RayTracer.Vector(5, 10, -1),
    894         new Flog.RayTracer.Color(0.8, 0.8, 0.8)
    895     );
    896 
    897     var light1 = new Flog.RayTracer.Light(
    898         new Flog.RayTracer.Vector(-3, 5, -15),
    899         new Flog.RayTracer.Color(0.8, 0.8, 0.8),
    900         100
    901     );
    902 
    903     scene.lights.push(light);
    904     scene.lights.push(light1);
    905 
    906     var imageWidth = 100; // $F('imageWidth');
    907     var imageHeight = 100; // $F('imageHeight');
    908     var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
    909     var renderDiffuse = true; // $F('renderDiffuse');
    910     var renderShadows = true; // $F('renderShadows');
    911     var renderHighlights = true; // $F('renderHighlights');
    912     var renderReflections = true; // $F('renderReflections');
    913     var rayDepth = 2;//$F('rayDepth');
    914 
    915     var raytracer = new Flog.RayTracer.Engine(
    916         {
    917             canvasWidth: imageWidth,
    918             canvasHeight: imageHeight,
    919             pixelWidth: pixelSize[0],
    920             pixelHeight: pixelSize[1],
    921             "renderDiffuse": renderDiffuse,
    922             "renderHighlights": renderHighlights,
    923             "renderShadows": renderShadows,
    924             "renderReflections": renderReflections,
    925             "rayDepth": rayDepth
    926         }
    927     );
    928 
    929     raytracer.renderScene(scene, null, 0);
    930 }
    931 
    932 for (var i = 0; i < 6; ++i)
    933   renderScene();
    934