1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/v8/raytrace.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,904 @@ 1.4 +// The ray tracer code in this file is written by Adam Burmister. It 1.5 +// is available in its original form from: 1.6 +// 1.7 +// http://labs.flog.nz.co/raytracer/ 1.8 +// 1.9 +// It has been modified slightly by Google to work as a standalone 1.10 +// benchmark, but the all the computational code remains 1.11 +// untouched. This file also contains a copy of parts of the Prototype 1.12 +// JavaScript framework which is used by the ray tracer. 1.13 + 1.14 +var RayTrace = new BenchmarkSuite('RayTrace', 739989, [ 1.15 + new Benchmark('RayTrace', renderScene) 1.16 +]); 1.17 + 1.18 + 1.19 +// Variable used to hold a number that can be used to verify that 1.20 +// the scene was ray traced correctly. 1.21 +var checkNumber; 1.22 + 1.23 + 1.24 +// ------------------------------------------------------------------------ 1.25 +// ------------------------------------------------------------------------ 1.26 + 1.27 +// The following is a copy of parts of the Prototype JavaScript library: 1.28 + 1.29 +// Prototype JavaScript framework, version 1.5.0 1.30 +// (c) 2005-2007 Sam Stephenson 1.31 +// 1.32 +// Prototype is freely distributable under the terms of an MIT-style license. 1.33 +// For details, see the Prototype web site: http://prototype.conio.net/ 1.34 + 1.35 + 1.36 +var Class = { 1.37 + create: function() { 1.38 + return function() { 1.39 + this.initialize.apply(this, arguments); 1.40 + } 1.41 + } 1.42 +}; 1.43 + 1.44 + 1.45 +Object.extend = function(destination, source) { 1.46 + for (var property in source) { 1.47 + destination[property] = source[property]; 1.48 + } 1.49 + return destination; 1.50 +}; 1.51 + 1.52 + 1.53 +// ------------------------------------------------------------------------ 1.54 +// ------------------------------------------------------------------------ 1.55 + 1.56 +// The rest of this file is the actual ray tracer written by Adam 1.57 +// Burmister. It's a concatenation of the following files: 1.58 +// 1.59 +// flog/color.js 1.60 +// flog/light.js 1.61 +// flog/vector.js 1.62 +// flog/ray.js 1.63 +// flog/scene.js 1.64 +// flog/material/basematerial.js 1.65 +// flog/material/solid.js 1.66 +// flog/material/chessboard.js 1.67 +// flog/shape/baseshape.js 1.68 +// flog/shape/sphere.js 1.69 +// flog/shape/plane.js 1.70 +// flog/intersectioninfo.js 1.71 +// flog/camera.js 1.72 +// flog/background.js 1.73 +// flog/engine.js 1.74 + 1.75 + 1.76 +/* Fake a Flog.* namespace */ 1.77 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.78 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.79 + 1.80 +Flog.RayTracer.Color = Class.create(); 1.81 + 1.82 +Flog.RayTracer.Color.prototype = { 1.83 + red : 0.0, 1.84 + green : 0.0, 1.85 + blue : 0.0, 1.86 + 1.87 + initialize : function(r, g, b) { 1.88 + if(!r) r = 0.0; 1.89 + if(!g) g = 0.0; 1.90 + if(!b) b = 0.0; 1.91 + 1.92 + this.red = r; 1.93 + this.green = g; 1.94 + this.blue = b; 1.95 + }, 1.96 + 1.97 + add : function(c1, c2){ 1.98 + var result = new Flog.RayTracer.Color(0,0,0); 1.99 + 1.100 + result.red = c1.red + c2.red; 1.101 + result.green = c1.green + c2.green; 1.102 + result.blue = c1.blue + c2.blue; 1.103 + 1.104 + return result; 1.105 + }, 1.106 + 1.107 + addScalar: function(c1, s){ 1.108 + var result = new Flog.RayTracer.Color(0,0,0); 1.109 + 1.110 + result.red = c1.red + s; 1.111 + result.green = c1.green + s; 1.112 + result.blue = c1.blue + s; 1.113 + 1.114 + result.limit(); 1.115 + 1.116 + return result; 1.117 + }, 1.118 + 1.119 + subtract: function(c1, c2){ 1.120 + var result = new Flog.RayTracer.Color(0,0,0); 1.121 + 1.122 + result.red = c1.red - c2.red; 1.123 + result.green = c1.green - c2.green; 1.124 + result.blue = c1.blue - c2.blue; 1.125 + 1.126 + return result; 1.127 + }, 1.128 + 1.129 + multiply : function(c1, c2) { 1.130 + var result = new Flog.RayTracer.Color(0,0,0); 1.131 + 1.132 + result.red = c1.red * c2.red; 1.133 + result.green = c1.green * c2.green; 1.134 + result.blue = c1.blue * c2.blue; 1.135 + 1.136 + return result; 1.137 + }, 1.138 + 1.139 + multiplyScalar : function(c1, f) { 1.140 + var result = new Flog.RayTracer.Color(0,0,0); 1.141 + 1.142 + result.red = c1.red * f; 1.143 + result.green = c1.green * f; 1.144 + result.blue = c1.blue * f; 1.145 + 1.146 + return result; 1.147 + }, 1.148 + 1.149 + divideFactor : function(c1, f) { 1.150 + var result = new Flog.RayTracer.Color(0,0,0); 1.151 + 1.152 + result.red = c1.red / f; 1.153 + result.green = c1.green / f; 1.154 + result.blue = c1.blue / f; 1.155 + 1.156 + return result; 1.157 + }, 1.158 + 1.159 + limit: function(){ 1.160 + this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0; 1.161 + this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0; 1.162 + this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0; 1.163 + }, 1.164 + 1.165 + distance : function(color) { 1.166 + var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue); 1.167 + return d; 1.168 + }, 1.169 + 1.170 + blend: function(c1, c2, w){ 1.171 + var result = new Flog.RayTracer.Color(0,0,0); 1.172 + result = Flog.RayTracer.Color.prototype.add( 1.173 + Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w), 1.174 + Flog.RayTracer.Color.prototype.multiplyScalar(c2, w) 1.175 + ); 1.176 + return result; 1.177 + }, 1.178 + 1.179 + brightness : function() { 1.180 + var r = Math.floor(this.red*255); 1.181 + var g = Math.floor(this.green*255); 1.182 + var b = Math.floor(this.blue*255); 1.183 + return (r * 77 + g * 150 + b * 29) >> 8; 1.184 + }, 1.185 + 1.186 + toString : function () { 1.187 + var r = Math.floor(this.red*255); 1.188 + var g = Math.floor(this.green*255); 1.189 + var b = Math.floor(this.blue*255); 1.190 + 1.191 + return "rgb("+ r +","+ g +","+ b +")"; 1.192 + } 1.193 +} 1.194 +/* Fake a Flog.* namespace */ 1.195 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.196 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.197 + 1.198 +Flog.RayTracer.Light = Class.create(); 1.199 + 1.200 +Flog.RayTracer.Light.prototype = { 1.201 + position: null, 1.202 + color: null, 1.203 + intensity: 10.0, 1.204 + 1.205 + initialize : function(pos, color, intensity) { 1.206 + this.position = pos; 1.207 + this.color = color; 1.208 + this.intensity = (intensity ? intensity : 10.0); 1.209 + }, 1.210 + 1.211 + toString : function () { 1.212 + return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']'; 1.213 + } 1.214 +} 1.215 +/* Fake a Flog.* namespace */ 1.216 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.217 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.218 + 1.219 +Flog.RayTracer.Vector = Class.create(); 1.220 + 1.221 +Flog.RayTracer.Vector.prototype = { 1.222 + x : 0.0, 1.223 + y : 0.0, 1.224 + z : 0.0, 1.225 + 1.226 + initialize : function(x, y, z) { 1.227 + this.x = (x ? x : 0); 1.228 + this.y = (y ? y : 0); 1.229 + this.z = (z ? z : 0); 1.230 + }, 1.231 + 1.232 + copy: function(vector){ 1.233 + this.x = vector.x; 1.234 + this.y = vector.y; 1.235 + this.z = vector.z; 1.236 + }, 1.237 + 1.238 + normalize : function() { 1.239 + var m = this.magnitude(); 1.240 + return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m); 1.241 + }, 1.242 + 1.243 + magnitude : function() { 1.244 + return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z)); 1.245 + }, 1.246 + 1.247 + cross : function(w) { 1.248 + return new Flog.RayTracer.Vector( 1.249 + -this.z * w.y + this.y * w.z, 1.250 + this.z * w.x - this.x * w.z, 1.251 + -this.y * w.x + this.x * w.y); 1.252 + }, 1.253 + 1.254 + dot : function(w) { 1.255 + return this.x * w.x + this.y * w.y + this.z * w.z; 1.256 + }, 1.257 + 1.258 + add : function(v, w) { 1.259 + return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z); 1.260 + }, 1.261 + 1.262 + subtract : function(v, w) { 1.263 + if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']'; 1.264 + return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z); 1.265 + }, 1.266 + 1.267 + multiplyVector : function(v, w) { 1.268 + return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z); 1.269 + }, 1.270 + 1.271 + multiplyScalar : function(v, w) { 1.272 + return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w); 1.273 + }, 1.274 + 1.275 + toString : function () { 1.276 + return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']'; 1.277 + } 1.278 +} 1.279 +/* Fake a Flog.* namespace */ 1.280 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.281 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.282 + 1.283 +Flog.RayTracer.Ray = Class.create(); 1.284 + 1.285 +Flog.RayTracer.Ray.prototype = { 1.286 + position : null, 1.287 + direction : null, 1.288 + initialize : function(pos, dir) { 1.289 + this.position = pos; 1.290 + this.direction = dir; 1.291 + }, 1.292 + 1.293 + toString : function () { 1.294 + return 'Ray [' + this.position + ',' + this.direction + ']'; 1.295 + } 1.296 +} 1.297 +/* Fake a Flog.* namespace */ 1.298 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.299 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.300 + 1.301 +Flog.RayTracer.Scene = Class.create(); 1.302 + 1.303 +Flog.RayTracer.Scene.prototype = { 1.304 + camera : null, 1.305 + shapes : [], 1.306 + lights : [], 1.307 + background : null, 1.308 + 1.309 + initialize : function() { 1.310 + this.camera = new Flog.RayTracer.Camera( 1.311 + new Flog.RayTracer.Vector(0,0,-5), 1.312 + new Flog.RayTracer.Vector(0,0,1), 1.313 + new Flog.RayTracer.Vector(0,1,0) 1.314 + ); 1.315 + this.shapes = new Array(); 1.316 + this.lights = new Array(); 1.317 + this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2); 1.318 + } 1.319 +} 1.320 +/* Fake a Flog.* namespace */ 1.321 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.322 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.323 +if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {}; 1.324 + 1.325 +Flog.RayTracer.Material.BaseMaterial = Class.create(); 1.326 + 1.327 +Flog.RayTracer.Material.BaseMaterial.prototype = { 1.328 + 1.329 + gloss: 2.0, // [0...infinity] 0 = matt 1.330 + transparency: 0.0, // 0=opaque 1.331 + reflection: 0.0, // [0...infinity] 0 = no reflection 1.332 + refraction: 0.50, 1.333 + hasTexture: false, 1.334 + 1.335 + initialize : function() { 1.336 + 1.337 + }, 1.338 + 1.339 + getColor: function(u, v){ 1.340 + 1.341 + }, 1.342 + 1.343 + wrapUp: function(t){ 1.344 + t = t % 2.0; 1.345 + if(t < -1) t += 2.0; 1.346 + if(t >= 1) t -= 2.0; 1.347 + return t; 1.348 + }, 1.349 + 1.350 + toString : function () { 1.351 + return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; 1.352 + } 1.353 +} 1.354 +/* Fake a Flog.* namespace */ 1.355 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.356 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.357 + 1.358 +Flog.RayTracer.Material.Solid = Class.create(); 1.359 + 1.360 +Flog.RayTracer.Material.Solid.prototype = Object.extend( 1.361 + new Flog.RayTracer.Material.BaseMaterial(), { 1.362 + initialize : function(color, reflection, refraction, transparency, gloss) { 1.363 + this.color = color; 1.364 + this.reflection = reflection; 1.365 + this.transparency = transparency; 1.366 + this.gloss = gloss; 1.367 + this.hasTexture = false; 1.368 + }, 1.369 + 1.370 + getColor: function(u, v){ 1.371 + return this.color; 1.372 + }, 1.373 + 1.374 + toString : function () { 1.375 + return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; 1.376 + } 1.377 + } 1.378 +); 1.379 +/* Fake a Flog.* namespace */ 1.380 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.381 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.382 + 1.383 +Flog.RayTracer.Material.Chessboard = Class.create(); 1.384 + 1.385 +Flog.RayTracer.Material.Chessboard.prototype = Object.extend( 1.386 + new Flog.RayTracer.Material.BaseMaterial(), { 1.387 + colorEven: null, 1.388 + colorOdd: null, 1.389 + density: 0.5, 1.390 + 1.391 + initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) { 1.392 + this.colorEven = colorEven; 1.393 + this.colorOdd = colorOdd; 1.394 + this.reflection = reflection; 1.395 + this.transparency = transparency; 1.396 + this.gloss = gloss; 1.397 + this.density = density; 1.398 + this.hasTexture = true; 1.399 + }, 1.400 + 1.401 + getColor: function(u, v){ 1.402 + var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density); 1.403 + 1.404 + if(t < 0.0) 1.405 + return this.colorEven; 1.406 + else 1.407 + return this.colorOdd; 1.408 + }, 1.409 + 1.410 + toString : function () { 1.411 + return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; 1.412 + } 1.413 + } 1.414 +); 1.415 +/* Fake a Flog.* namespace */ 1.416 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.417 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.418 +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; 1.419 + 1.420 +Flog.RayTracer.Shape.Sphere = Class.create(); 1.421 + 1.422 +Flog.RayTracer.Shape.Sphere.prototype = { 1.423 + initialize : function(pos, radius, material) { 1.424 + this.radius = radius; 1.425 + this.position = pos; 1.426 + this.material = material; 1.427 + }, 1.428 + 1.429 + intersect: function(ray){ 1.430 + var info = new Flog.RayTracer.IntersectionInfo(); 1.431 + info.shape = this; 1.432 + 1.433 + var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position); 1.434 + 1.435 + var B = dst.dot(ray.direction); 1.436 + var C = dst.dot(dst) - (this.radius * this.radius); 1.437 + var D = (B * B) - C; 1.438 + 1.439 + if(D > 0){ // intersection! 1.440 + info.isHit = true; 1.441 + info.distance = (-B) - Math.sqrt(D); 1.442 + info.position = Flog.RayTracer.Vector.prototype.add( 1.443 + ray.position, 1.444 + Flog.RayTracer.Vector.prototype.multiplyScalar( 1.445 + ray.direction, 1.446 + info.distance 1.447 + ) 1.448 + ); 1.449 + info.normal = Flog.RayTracer.Vector.prototype.subtract( 1.450 + info.position, 1.451 + this.position 1.452 + ).normalize(); 1.453 + 1.454 + info.color = this.material.getColor(0,0); 1.455 + } else { 1.456 + info.isHit = false; 1.457 + } 1.458 + return info; 1.459 + }, 1.460 + 1.461 + toString : function () { 1.462 + return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']'; 1.463 + } 1.464 +} 1.465 +/* Fake a Flog.* namespace */ 1.466 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.467 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.468 +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; 1.469 + 1.470 +Flog.RayTracer.Shape.Plane = Class.create(); 1.471 + 1.472 +Flog.RayTracer.Shape.Plane.prototype = { 1.473 + d: 0.0, 1.474 + 1.475 + initialize : function(pos, d, material) { 1.476 + this.position = pos; 1.477 + this.d = d; 1.478 + this.material = material; 1.479 + }, 1.480 + 1.481 + intersect: function(ray){ 1.482 + var info = new Flog.RayTracer.IntersectionInfo(); 1.483 + 1.484 + var Vd = this.position.dot(ray.direction); 1.485 + if(Vd == 0) return info; // no intersection 1.486 + 1.487 + var t = -(this.position.dot(ray.position) + this.d) / Vd; 1.488 + if(t <= 0) return info; 1.489 + 1.490 + info.shape = this; 1.491 + info.isHit = true; 1.492 + info.position = Flog.RayTracer.Vector.prototype.add( 1.493 + ray.position, 1.494 + Flog.RayTracer.Vector.prototype.multiplyScalar( 1.495 + ray.direction, 1.496 + t 1.497 + ) 1.498 + ); 1.499 + info.normal = this.position; 1.500 + info.distance = t; 1.501 + 1.502 + if(this.material.hasTexture){ 1.503 + var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x); 1.504 + var vV = vU.cross(this.position); 1.505 + var u = info.position.dot(vU); 1.506 + var v = info.position.dot(vV); 1.507 + info.color = this.material.getColor(u,v); 1.508 + } else { 1.509 + info.color = this.material.getColor(0,0); 1.510 + } 1.511 + 1.512 + return info; 1.513 + }, 1.514 + 1.515 + toString : function () { 1.516 + return 'Plane [' + this.position + ', d=' + this.d + ']'; 1.517 + } 1.518 +} 1.519 +/* Fake a Flog.* namespace */ 1.520 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.521 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.522 + 1.523 +Flog.RayTracer.IntersectionInfo = Class.create(); 1.524 + 1.525 +Flog.RayTracer.IntersectionInfo.prototype = { 1.526 + isHit: false, 1.527 + hitCount: 0, 1.528 + shape: null, 1.529 + position: null, 1.530 + normal: null, 1.531 + color: null, 1.532 + distance: null, 1.533 + 1.534 + initialize : function() { 1.535 + this.color = new Flog.RayTracer.Color(0,0,0); 1.536 + }, 1.537 + 1.538 + toString : function () { 1.539 + return 'Intersection [' + this.position + ']'; 1.540 + } 1.541 +} 1.542 +/* Fake a Flog.* namespace */ 1.543 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.544 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.545 + 1.546 +Flog.RayTracer.Camera = Class.create(); 1.547 + 1.548 +Flog.RayTracer.Camera.prototype = { 1.549 + position: null, 1.550 + lookAt: null, 1.551 + equator: null, 1.552 + up: null, 1.553 + screen: null, 1.554 + 1.555 + initialize : function(pos, lookAt, up) { 1.556 + this.position = pos; 1.557 + this.lookAt = lookAt; 1.558 + this.up = up; 1.559 + this.equator = lookAt.normalize().cross(this.up); 1.560 + this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt); 1.561 + }, 1.562 + 1.563 + getRay: function(vx, vy){ 1.564 + var pos = Flog.RayTracer.Vector.prototype.subtract( 1.565 + this.screen, 1.566 + Flog.RayTracer.Vector.prototype.subtract( 1.567 + Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx), 1.568 + Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy) 1.569 + ) 1.570 + ); 1.571 + pos.y = pos.y * -1; 1.572 + var dir = Flog.RayTracer.Vector.prototype.subtract( 1.573 + pos, 1.574 + this.position 1.575 + ); 1.576 + 1.577 + var ray = new Flog.RayTracer.Ray(pos, dir.normalize()); 1.578 + 1.579 + return ray; 1.580 + }, 1.581 + 1.582 + toString : function () { 1.583 + return 'Ray []'; 1.584 + } 1.585 +} 1.586 +/* Fake a Flog.* namespace */ 1.587 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.588 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.589 + 1.590 +Flog.RayTracer.Background = Class.create(); 1.591 + 1.592 +Flog.RayTracer.Background.prototype = { 1.593 + color : null, 1.594 + ambience : 0.0, 1.595 + 1.596 + initialize : function(color, ambience) { 1.597 + this.color = color; 1.598 + this.ambience = ambience; 1.599 + } 1.600 +} 1.601 +/* Fake a Flog.* namespace */ 1.602 +if(typeof(Flog) == 'undefined') var Flog = {}; 1.603 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; 1.604 + 1.605 +Flog.RayTracer.Engine = Class.create(); 1.606 + 1.607 +Flog.RayTracer.Engine.prototype = { 1.608 + canvas: null, /* 2d context we can render to */ 1.609 + 1.610 + initialize: function(options){ 1.611 + this.options = Object.extend({ 1.612 + canvasHeight: 100, 1.613 + canvasWidth: 100, 1.614 + pixelWidth: 2, 1.615 + pixelHeight: 2, 1.616 + renderDiffuse: false, 1.617 + renderShadows: false, 1.618 + renderHighlights: false, 1.619 + renderReflections: false, 1.620 + rayDepth: 2 1.621 + }, options || {}); 1.622 + 1.623 + this.options.canvasHeight /= this.options.pixelHeight; 1.624 + this.options.canvasWidth /= this.options.pixelWidth; 1.625 + 1.626 + /* TODO: dynamically include other scripts */ 1.627 + }, 1.628 + 1.629 + setPixel: function(x, y, color){ 1.630 + var pxW, pxH; 1.631 + pxW = this.options.pixelWidth; 1.632 + pxH = this.options.pixelHeight; 1.633 + 1.634 + if (this.canvas) { 1.635 + this.canvas.fillStyle = color.toString(); 1.636 + this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH); 1.637 + } else { 1.638 + if (x === y) { 1.639 + checkNumber += color.brightness(); 1.640 + } 1.641 + // print(x * pxW, y * pxH, pxW, pxH); 1.642 + } 1.643 + }, 1.644 + 1.645 + renderScene: function(scene, canvas){ 1.646 + checkNumber = 0; 1.647 + /* Get canvas */ 1.648 + if (canvas) { 1.649 + this.canvas = canvas.getContext("2d"); 1.650 + } else { 1.651 + this.canvas = null; 1.652 + } 1.653 + 1.654 + var canvasHeight = this.options.canvasHeight; 1.655 + var canvasWidth = this.options.canvasWidth; 1.656 + 1.657 + for(var y=0; y < canvasHeight; y++){ 1.658 + for(var x=0; x < canvasWidth; x++){ 1.659 + var yp = y * 1.0 / canvasHeight * 2 - 1; 1.660 + var xp = x * 1.0 / canvasWidth * 2 - 1; 1.661 + 1.662 + var ray = scene.camera.getRay(xp, yp); 1.663 + 1.664 + var color = this.getPixelColor(ray, scene); 1.665 + 1.666 + this.setPixel(x, y, color); 1.667 + } 1.668 + } 1.669 + if (checkNumber !== 2321) { 1.670 + throw new Error("Scene rendered incorrectly"); 1.671 + } 1.672 + }, 1.673 + 1.674 + getPixelColor: function(ray, scene){ 1.675 + var info = this.testIntersection(ray, scene, null); 1.676 + if(info.isHit){ 1.677 + var color = this.rayTrace(info, ray, scene, 0); 1.678 + return color; 1.679 + } 1.680 + return scene.background.color; 1.681 + }, 1.682 + 1.683 + testIntersection: function(ray, scene, exclude){ 1.684 + var hits = 0; 1.685 + var best = new Flog.RayTracer.IntersectionInfo(); 1.686 + best.distance = 2000; 1.687 + 1.688 + for(var i=0; i<scene.shapes.length; i++){ 1.689 + var shape = scene.shapes[i]; 1.690 + 1.691 + if(shape != exclude){ 1.692 + var info = shape.intersect(ray); 1.693 + if(info.isHit && info.distance >= 0 && info.distance < best.distance){ 1.694 + best = info; 1.695 + hits++; 1.696 + } 1.697 + } 1.698 + } 1.699 + best.hitCount = hits; 1.700 + return best; 1.701 + }, 1.702 + 1.703 + getReflectionRay: function(P,N,V){ 1.704 + var c1 = -N.dot(V); 1.705 + var R1 = Flog.RayTracer.Vector.prototype.add( 1.706 + Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1), 1.707 + V 1.708 + ); 1.709 + return new Flog.RayTracer.Ray(P, R1); 1.710 + }, 1.711 + 1.712 + rayTrace: function(info, ray, scene, depth){ 1.713 + // Calc ambient 1.714 + var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience); 1.715 + var oldColor = color; 1.716 + var shininess = Math.pow(10, info.shape.material.gloss + 1); 1.717 + 1.718 + for(var i=0; i<scene.lights.length; i++){ 1.719 + var light = scene.lights[i]; 1.720 + 1.721 + // Calc diffuse lighting 1.722 + var v = Flog.RayTracer.Vector.prototype.subtract( 1.723 + light.position, 1.724 + info.position 1.725 + ).normalize(); 1.726 + 1.727 + if(this.options.renderDiffuse){ 1.728 + var L = v.dot(info.normal); 1.729 + if(L > 0.0){ 1.730 + color = Flog.RayTracer.Color.prototype.add( 1.731 + color, 1.732 + Flog.RayTracer.Color.prototype.multiply( 1.733 + info.color, 1.734 + Flog.RayTracer.Color.prototype.multiplyScalar( 1.735 + light.color, 1.736 + L 1.737 + ) 1.738 + ) 1.739 + ); 1.740 + } 1.741 + } 1.742 + 1.743 + // The greater the depth the more accurate the colours, but 1.744 + // this is exponentially (!) expensive 1.745 + if(depth <= this.options.rayDepth){ 1.746 + // calculate reflection ray 1.747 + if(this.options.renderReflections && info.shape.material.reflection > 0) 1.748 + { 1.749 + var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction); 1.750 + var refl = this.testIntersection(reflectionRay, scene, info.shape); 1.751 + 1.752 + if (refl.isHit && refl.distance > 0){ 1.753 + refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1); 1.754 + } else { 1.755 + refl.color = scene.background.color; 1.756 + } 1.757 + 1.758 + color = Flog.RayTracer.Color.prototype.blend( 1.759 + color, 1.760 + refl.color, 1.761 + info.shape.material.reflection 1.762 + ); 1.763 + } 1.764 + 1.765 + // Refraction 1.766 + /* TODO */ 1.767 + } 1.768 + 1.769 + /* Render shadows and highlights */ 1.770 + 1.771 + var shadowInfo = new Flog.RayTracer.IntersectionInfo(); 1.772 + 1.773 + if(this.options.renderShadows){ 1.774 + var shadowRay = new Flog.RayTracer.Ray(info.position, v); 1.775 + 1.776 + shadowInfo = this.testIntersection(shadowRay, scene, info.shape); 1.777 + if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){ 1.778 + var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5); 1.779 + var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5)); 1.780 + color = Flog.RayTracer.Color.prototype.addScalar(vA,dB); 1.781 + } 1.782 + } 1.783 + 1.784 + // Phong specular highlights 1.785 + if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){ 1.786 + var Lv = Flog.RayTracer.Vector.prototype.subtract( 1.787 + info.shape.position, 1.788 + light.position 1.789 + ).normalize(); 1.790 + 1.791 + var E = Flog.RayTracer.Vector.prototype.subtract( 1.792 + scene.camera.position, 1.793 + info.shape.position 1.794 + ).normalize(); 1.795 + 1.796 + var H = Flog.RayTracer.Vector.prototype.subtract( 1.797 + E, 1.798 + Lv 1.799 + ).normalize(); 1.800 + 1.801 + var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess); 1.802 + color = Flog.RayTracer.Color.prototype.add( 1.803 + Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight), 1.804 + color 1.805 + ); 1.806 + } 1.807 + } 1.808 + color.limit(); 1.809 + return color; 1.810 + } 1.811 +}; 1.812 + 1.813 + 1.814 +function renderScene(){ 1.815 + var scene = new Flog.RayTracer.Scene(); 1.816 + 1.817 + scene.camera = new Flog.RayTracer.Camera( 1.818 + new Flog.RayTracer.Vector(0, 0, -15), 1.819 + new Flog.RayTracer.Vector(-0.2, 0, 5), 1.820 + new Flog.RayTracer.Vector(0, 1, 0) 1.821 + ); 1.822 + 1.823 + scene.background = new Flog.RayTracer.Background( 1.824 + new Flog.RayTracer.Color(0.5, 0.5, 0.5), 1.825 + 0.4 1.826 + ); 1.827 + 1.828 + var sphere = new Flog.RayTracer.Shape.Sphere( 1.829 + new Flog.RayTracer.Vector(-1.5, 1.5, 2), 1.830 + 1.5, 1.831 + new Flog.RayTracer.Material.Solid( 1.832 + new Flog.RayTracer.Color(0,0.5,0.5), 1.833 + 0.3, 1.834 + 0.0, 1.835 + 0.0, 1.836 + 2.0 1.837 + ) 1.838 + ); 1.839 + 1.840 + var sphere1 = new Flog.RayTracer.Shape.Sphere( 1.841 + new Flog.RayTracer.Vector(1, 0.25, 1), 1.842 + 0.5, 1.843 + new Flog.RayTracer.Material.Solid( 1.844 + new Flog.RayTracer.Color(0.9,0.9,0.9), 1.845 + 0.1, 1.846 + 0.0, 1.847 + 0.0, 1.848 + 1.5 1.849 + ) 1.850 + ); 1.851 + 1.852 + var plane = new Flog.RayTracer.Shape.Plane( 1.853 + new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(), 1.854 + 1.2, 1.855 + new Flog.RayTracer.Material.Chessboard( 1.856 + new Flog.RayTracer.Color(1,1,1), 1.857 + new Flog.RayTracer.Color(0,0,0), 1.858 + 0.2, 1.859 + 0.0, 1.860 + 1.0, 1.861 + 0.7 1.862 + ) 1.863 + ); 1.864 + 1.865 + scene.shapes.push(plane); 1.866 + scene.shapes.push(sphere); 1.867 + scene.shapes.push(sphere1); 1.868 + 1.869 + var light = new Flog.RayTracer.Light( 1.870 + new Flog.RayTracer.Vector(5, 10, -1), 1.871 + new Flog.RayTracer.Color(0.8, 0.8, 0.8) 1.872 + ); 1.873 + 1.874 + var light1 = new Flog.RayTracer.Light( 1.875 + new Flog.RayTracer.Vector(-3, 5, -15), 1.876 + new Flog.RayTracer.Color(0.8, 0.8, 0.8), 1.877 + 100 1.878 + ); 1.879 + 1.880 + scene.lights.push(light); 1.881 + scene.lights.push(light1); 1.882 + 1.883 + var imageWidth = 100; // $F('imageWidth'); 1.884 + var imageHeight = 100; // $F('imageHeight'); 1.885 + var pixelSize = "5,5".split(','); // $F('pixelSize').split(','); 1.886 + var renderDiffuse = true; // $F('renderDiffuse'); 1.887 + var renderShadows = true; // $F('renderShadows'); 1.888 + var renderHighlights = true; // $F('renderHighlights'); 1.889 + var renderReflections = true; // $F('renderReflections'); 1.890 + var rayDepth = 2;//$F('rayDepth'); 1.891 + 1.892 + var raytracer = new Flog.RayTracer.Engine( 1.893 + { 1.894 + canvasWidth: imageWidth, 1.895 + canvasHeight: imageHeight, 1.896 + pixelWidth: pixelSize[0], 1.897 + pixelHeight: pixelSize[1], 1.898 + "renderDiffuse": renderDiffuse, 1.899 + "renderHighlights": renderHighlights, 1.900 + "renderShadows": renderShadows, 1.901 + "renderReflections": renderReflections, 1.902 + "rayDepth": rayDepth 1.903 + } 1.904 + ); 1.905 + 1.906 + raytracer.renderScene(scene, null, 0); 1.907 +}