js/src/v8/raytrace.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial