js/src/v8/raytrace.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial