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