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