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