C#
using System;
using Dom = DuoCode.Dom;
using DuoCode.Runtime;
namespace RayTracer
{
struct Vector
{
public readonly double x;
public readonly double y;
public readonly double z;
public Vector(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector operator *(Vector v, double k)
{
return new Vector(v.x * k, v.y * k, v.z * k);
}
public static Vector operator -(Vector v1, Vector v2)
{
return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
public static Vector operator +(Vector v1, Vector v2)
{
return new Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
public static double dot(Vector v1, Vector v2)
{
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
public static double mag(Vector v)
{
return Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
public static Vector norm(Vector v)
{
double mag = Vector.mag(v);
double div = (mag == 0) ? double.PositiveInfinity : 1.0 / mag;
return v * div;
}
public static Vector cross(Vector v1, Vector v2)
{
return new Vector(v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x);
}
}
struct Color
{
public readonly double r;
public readonly double g;
public readonly double b;
public Color(double r, double g, double b)
{
this.r = r;
this.g = g;
this.b = b;
}
public static Color scale(double k, Color v)
{
return new Color(k * v.r, k * v.g, k * v.b);
}
public static Color plus(Color v1, Color v2)
{
return new Color(v1.r + v2.r, v1.g + v2.g, v1.b + v2.b);
}
public static Color times(Color v1, Color v2)
{
return new Color(v1.r * v2.r, v1.g * v2.g, v1.b * v2.b);
}
public static Color white = new Color(1.0, 1.0, 1.0);
public static Color grey = new Color(0.5, 0.5, 0.5);
public static Color black = new Color(0.0, 0.0, 0.0);
public static Color background = Color.black;
public static Color defaultColor = Color.black;
private int ToByte(double value)
{
value = value > 1 ? 1 : value;
return (int)Math.Floor(value * 255);
}
public override string ToString()
{
return string.Format("rgb({0},{1},{2})", ToByte(r), ToByte(g), ToByte(b));
}
}
class Camera
{
public readonly Vector forward;
public readonly Vector right;
public readonly Vector up;
public readonly Vector pos;
public readonly Vector lookAt;
public Camera(Vector pos, Vector lookAt)
{
this.pos = pos;
this.lookAt = lookAt;
Vector down = new Vector(0.0, -1.0, 0.0);
this.forward = Vector.norm(lookAt - pos);
this.right = Vector.norm(Vector.cross(this.forward, down)) * 1.5;
this.up = Vector.norm(Vector.cross(this.forward, this.right)) * 1.5;
}
}
class Ray
{
public Vector start;
public Vector dir;
}
class Intersection
{
public Thing thing;
public Ray ray;
public double dist;
}
class Surface
{
public Func diffuse;
public Func specular;
public Func reflect;
public double roughness;
}
interface Thing
{
Intersection intersect(Ray ray);
Vector normal(Vector pos);
Surface surface { get; }
}
class Light
{
public Vector pos;
public Color color;
}
class Scene
{
public Thing[] things;
public Light[] lights;
public Camera camera;
}
class Sphere : Thing
{
private readonly Vector center;
private readonly double radius2;
private readonly Surface surface;
public Sphere(Vector center, double radius, Surface surface)
{
this.center = center;
this.surface = surface;
this.radius2 = radius * radius;
}
public Vector normal(Vector pos)
{
return Vector.norm(pos - this.center);
}
Surface Thing.surface
{
get
{
return this.surface;
}
}
public Intersection intersect(Ray ray)
{
var eo = this.center - ray.start;
var v = Vector.dot(eo, ray.dir);
var dist = 0d;
if (v >= 0)
{
var disc = this.radius2 - (Vector.dot(eo, eo) - v * v);
if (disc >= 0)
{
dist = v - Math.Sqrt(disc);
}
}
if (dist == 0)
return null;
else
return new Intersection { thing = this, ray = ray, dist = dist };
}
}
class Plane : Thing
{
private readonly Vector norm;
private readonly Surface surface;
private readonly double offset;
public Plane(Vector norm, double offset, Surface surface)
{
this.norm = norm;
this.offset = offset;
this.surface = surface;
}
public Intersection intersect(Ray ray)
{
Intersection result = null;
var denom = Vector.dot(norm, ray.dir);
if (denom <= 0)
{
var dist = (Vector.dot(norm, ray.start) + offset) / (-denom);
result = new Intersection { thing = this, ray = ray, dist = dist };
}
return result;
}
public Vector normal(Vector pos)
{
return norm;
}
Surface Thing.surface
{
get
{
return this.surface;
}
}
}
static class Surfaces
{
public static Surface shiny = new Surface
{
diffuse = (pos) => Color.white,
specular = (pos) => Color.grey,
reflect = (pos) => 0.7,
roughness = 250
};
public static Surface checkerboard = new Surface
{
diffuse = (pos) => ((Math.Floor(pos.z) + Math.Floor(pos.x)) % 2 != 0) ? Color.white : Color.black,
specular = (pos) => Color.white,
reflect = (pos) => ((Math.Floor(pos.z) + Math.Floor(pos.x)) % 2 != 0) ? 0.1 : 0.7,
roughness = 150
};
}
static class RayTracer
{
private const int maxDepth = 5;
private static Intersection intersections(Ray ray, Scene scene)
{
double closest = double.PositiveInfinity;
Intersection closestInter = null;
foreach (var thing in scene.things)
{
var inter = thing.intersect(ray);
if (inter != null && inter.dist < closest)
{
closestInter = inter;
closest = inter.dist;
}
}
return closestInter;
}
private static double testRay(Ray ray, Scene scene)
{
Intersection isect = intersections(ray, scene);
return (isect != null) ? isect.dist : double.NaN;
}
private static Color traceRay(Ray ray, Scene scene, int depth)
{
var isect = intersections(ray, scene);
return isect == null ? Color.background : shade(isect, scene, depth);
}
private static Color shade(Intersection isect, Scene scene, int depth)
{
var d = isect.ray.dir;
var pos = d * isect.dist + isect.ray.start;
var normal = isect.thing.normal(pos);
var reflectDir = d - (normal * 2 * Vector.dot(normal, d));
var naturalColor = Color.plus(Color.background, getNaturalColor(isect.thing, pos, normal, reflectDir, scene));
var reflectedColor = (depth >= maxDepth) ? Color.grey : getReflectionColor(isect.thing, pos, normal, reflectDir, scene, depth);
return Color.plus(naturalColor, reflectedColor);
}
private static Color getReflectionColor(Thing thing, Vector pos, Vector normal, Vector rd, Scene scene, int depth)
{
return Color.scale(thing.surface.reflect(pos), traceRay(new Ray { start = pos, dir = rd }, scene, depth + 1));
}
private static Color getNaturalColor(Thing thing, Vector pos, Vector norm, Vector rd, Scene scene)
{
Color result = Color.defaultColor;
foreach (Light light in scene.lights)
{
var ldis = light.pos - pos;
var livec = Vector.norm(ldis);
var neatIsect = testRay(new Ray { start = pos, dir = livec }, scene);
var isInShadow = !double.IsNaN(neatIsect) && (neatIsect <= Vector.mag(ldis));
if (isInShadow)
continue;
var illum = Vector.dot(livec, norm);
var lcolor = (illum > 0) ? Color.scale(illum, light.color) : Color.defaultColor;
var specular = Vector.dot(livec, Vector.norm(rd));
var scolor = (specular > 0) ? Color.scale(Math.Pow(specular, thing.surface.roughness), light.color) : Color.defaultColor;
result = Color.plus(result, Color.plus(Color.times(thing.surface.diffuse(pos), lcolor),
Color.times(thing.surface.specular(pos), scolor)));
}
return result;
}
public static void render(Scene scene, Dom.CanvasRenderingContext2D ctx, int screenWidth, int screenHeight)
{
Camera camera = scene.camera;
for (int y = 0; y < screenHeight; y++)
for (int x = 0; x < screenWidth; x++)
{
double recenterX = (x - (screenWidth / 2.0)) / 2.0 / screenWidth;
double recenterY = -(y - (screenHeight / 2.0)) / 2.0 / screenHeight;
Vector dir = Vector.norm(camera.forward + (camera.right * recenterX) + (camera.up * recenterY));
Color color = traceRay(new Ray { start = camera.pos, dir = dir }, scene, 0);
ctx.fillStyle = color.ToString();
ctx.fillRect(x, y, x + 1, y + 1);
}
}
}
static class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Hello RayTracer!");
Scene defaultScene = new Scene
{
things = new Thing[]
{
new Plane(new Vector(0.0, 1.0, 0.0), 0.0, Surfaces.checkerboard),
new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces.shiny),
new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces.shiny)
},
lights = new[]
{
new Light { pos = new Vector(-2.0, 2.5, 0.0), color = new Color(0.49, 0.07, 0.07) },
new Light { pos = new Vector(1.5, 2.5, 1.5), color = new Color(0.07, 0.07, 0.49) },
new Light { pos = new Vector(1.5, 2.5, -1.5), color = new Color(0.07, 0.49, 0.071) },
new Light { pos = new Vector(0.0, 3.5, 0.0), color = new Color(0.21, 0.21, 0.35) }
},
camera = new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0))
};
var canv = Dom.Global.document.createElement("canvas");
canv.width = 256;
canv.height = 256;
Dom.Global.document.body.appendChild(canv);
var ctx = canv.getContext("2d");
RayTracer.render(defaultScene, ctx, 256, 256);
}
}
}
JavaScript
"use strict";
var $RayTracer$Assembly = {
fullName: "RayTracer",
anonymousTypes: [],
types: [],
$getAttrs: function() {
return [new System.Reflection.AssemblyTitleAttribute.ctor("HelloDuoCode"), new System.Reflection.AssemblyDescriptionAttribute.ctor(""),
new System.Reflection.AssemblyConfigurationAttribute.ctor(""), new System.Reflection.AssemblyCompanyAttribute.ctor(""),
new System.Reflection.AssemblyProductAttribute.ctor("HelloDuoCode"), new System.Reflection.AssemblyCopyrightAttribute.ctor("Copyright \xA9 2014"),
new System.Reflection.AssemblyTrademarkAttribute.ctor(""), new System.Reflection.AssemblyCultureAttribute.ctor(""),
new System.Reflection.AssemblyVersionAttribute.ctor("1.0.0.0"), new System.Reflection.AssemblyFileVersionAttribute.ctor("1.0.0.0"),
new DuoCode.Runtime.CompilerAttribute.ctor("0.1.778.0 BETA")];
}
};
$assemblies.push($RayTracer$Assembly);
this.RayTracer = this.RayTracer || {};
RayTracer.Vector = $declare("RayTracer.Vector", null, 62, $RayTracer$Assembly, function($t, $p) {
$t.ctor = function Vector() {
this.x = 0;
this.y = 0;
this.z = 0;
};
$t.ctor.prototype = $p;
$t.ctor$1 = function Vector(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
$t.ctor$1.prototype = $p;
$t.op_Multiply = function Vector_op_Multiply(v, k) {
return new RayTracer.Vector.ctor$1(v.x * k, v.y * k, v.z * k);
};
$t.op_Subtraction = function Vector_op_Subtraction(v1, v2) {
return new RayTracer.Vector.ctor$1(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
};
$t.op_Addition = function Vector_op_Addition(v1, v2) {
return new RayTracer.Vector.ctor$1(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
};
$t.dot = function Vector_dot(v1, v2)
{
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
};
$t.mag = function Vector_mag(v)
{
return System.Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
};
$t.norm = function Vector_norm(v)
{
var mag = RayTracer.Vector.mag(v);
var div = (mag == 0) ? Infinity /* Double.PositiveInfinity */ : 1 / mag;
return RayTracer.Vector.op_Multiply(v, div);
};
$t.cross = function Vector_cross(v1, v2)
{
return new RayTracer.Vector.ctor$1(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
};
});
RayTracer.Color = $declare("RayTracer.Color", null, 62, $RayTracer$Assembly, function($t, $p) {
$t.cctor = function() {
$t.white = new RayTracer.Color.ctor$1(1, 1, 1);
$t.grey = new RayTracer.Color.ctor$1(0.5, 0.5, 0.5);
$t.black = new RayTracer.Color.ctor$1(0, 0, 0);
$t.background = RayTracer.Color().black;
$t.defaultColor = RayTracer.Color().black;
};
$t.ctor = function Color() {
this.r = 0;
this.g = 0;
this.b = 0;
};
$t.ctor.prototype = $p;
$t.ctor$1 = function Color(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
};
$t.ctor$1.prototype = $p;
$t.scale = function Color_scale(k, v)
{
return new RayTracer.Color.ctor$1(k * v.r, k * v.g, k * v.b);
};
$t.plus = function Color_plus(v1, v2)
{
return new RayTracer.Color.ctor$1(v1.r + v2.r, v1.g + v2.g, v1.b + v2.b);
};
$t.times = function Color_times(v1, v2)
{
return new RayTracer.Color.ctor$1(v1.r * v2.r, v1.g * v2.g, v1.b * v2.b);
};
$p.ToByte = function Color_ToByte(value)
{
value = value > 1 ? 1 : value;
return (System.Math.Floor$1(value * 255) | 0);
};
$p.ToString = function Color_ToString()
{
return String.Format("rgb({0},{1},{2})", $array(System.Object, [this.ToByte(this.r), this.ToByte(this.g),
this.ToByte(this.b)]));
};
});
RayTracer.Camera = $declare("RayTracer.Camera", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.forward = new RayTracer.Vector.ctor();
this.right = new RayTracer.Vector.ctor();
this.up = new RayTracer.Vector.ctor();
this.pos = new RayTracer.Vector.ctor();
this.lookAt = new RayTracer.Vector.ctor();
};
$t.ctor = function Camera(pos, lookAt) {
$t.$baseType.ctor.call(this);
this.pos = pos;
this.lookAt = lookAt;
var down = new RayTracer.Vector.ctor$1(0, -1, 0);
this.forward = RayTracer.Vector.norm(RayTracer.Vector.op_Subtraction(lookAt, pos));
this.right = RayTracer.Vector.op_Multiply(RayTracer.Vector.norm(RayTracer.Vector.cross(this.forward,
down)), 1.5);
this.up = RayTracer.Vector.op_Multiply(RayTracer.Vector.norm(RayTracer.Vector.cross(this.forward,
this.right)), 1.5);
};
$t.ctor.prototype = $p;
});
RayTracer.Ray = $declare("RayTracer.Ray", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.start = new RayTracer.Vector.ctor();
this.dir = new RayTracer.Vector.ctor();
};
$t.ctor = function Ray() {
$t.$baseType.ctor.call(this);
};
$t.ctor.prototype = $p;
});
RayTracer.Intersection = $declare("RayTracer.Intersection", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.thing = null;
this.ray = null;
this.dist = 0;
};
$t.ctor = function Intersection() {
$t.$baseType.ctor.call(this);
};
$t.ctor.prototype = $p;
});
RayTracer.Surface = $declare("RayTracer.Surface", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.diffuse = null;
this.specular = null;
this.reflect = null;
this.roughness = 0;
};
$t.ctor = function Surface() {
$t.$baseType.ctor.call(this);
};
$t.ctor.prototype = $p;
});
RayTracer.Thing = $declare("RayTracer.Thing", null, 66, $RayTracer$Assembly, function($t, $p) {});
RayTracer.Light = $declare("RayTracer.Light", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.pos = new RayTracer.Vector.ctor();
this.color = new RayTracer.Color.ctor();
};
$t.ctor = function Light() {
$t.$baseType.ctor.call(this);
};
$t.ctor.prototype = $p;
});
RayTracer.Scene = $declare("RayTracer.Scene", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$ator = function() {
this.things = null;
this.lights = null;
this.camera = null;
};
$t.ctor = function Scene() {
$t.$baseType.ctor.call(this);
};
$t.ctor.prototype = $p;
});
RayTracer.Sphere = $declare("RayTracer.Sphere", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$intfs = [RayTracer.Thing];
$t.$ator = function() {
this.center = new RayTracer.Vector.ctor();
this.radius2 = 0;
this.surface = null;
};
$t.ctor = function Sphere(center, radius, surface) {
$t.$baseType.ctor.call(this);
this.center = center;
this.surface = surface;
this.radius2 = radius * radius;
};
$t.ctor.prototype = $p;
$p.normal = function Sphere_normal(pos)
{
return RayTracer.Vector.norm(RayTracer.Vector.op_Subtraction(pos, this.center));
};
$p.RayTracer$Thing$get_surface = function Sphere_RayTracer$Thing$get_surface() {
return this.surface;
};
$p.intersect = function Sphere_intersect(ray)
{
var eo = RayTracer.Vector.op_Subtraction(this.center, ray.start);
var v = RayTracer.Vector.dot(eo, ray.dir);
var dist = 0;
if (v >= 0) {
var disc = this.radius2 - (RayTracer.Vector.dot(eo, eo) - v * v);
if (disc >= 0) {
dist = v - System.Math.Sqrt(disc);
}
}
if (dist == 0)
return null;
else
return (function() {
var $obj$ = new RayTracer.Intersection.ctor();
$obj$.thing = this;
$obj$.ray = ray;
$obj$.dist = dist;
return $obj$;
}).call(this);
};
$p.RayTracer$Thing$intersect = $p.intersect;
$p.RayTracer$Thing$normal = $p.normal;
});
RayTracer.Plane = $declare("RayTracer.Plane", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.$intfs = [RayTracer.Thing];
$t.$ator = function() {
this.norm = new RayTracer.Vector.ctor();
this.surface = null;
this.offset = 0;
};
$t.ctor = function Plane(norm, offset, surface) {
$t.$baseType.ctor.call(this);
this.norm = norm;
this.offset = offset;
this.surface = surface;
};
$t.ctor.prototype = $p;
$p.intersect = function Plane_intersect(ray)
{
var result = null;
var denom = RayTracer.Vector.dot(this.norm, ray.dir);
if (denom <= 0) {
var dist = (RayTracer.Vector.dot(this.norm, ray.start) + this.offset) / (-denom);
result = (function() {
var $obj$ = new RayTracer.Intersection.ctor();
$obj$.thing = this;
$obj$.ray = ray;
$obj$.dist = dist;
return $obj$;
}).call(this);
}
return result;
};
$p.normal = function Plane_normal(pos)
{
return this.norm;
};
$p.RayTracer$Thing$get_surface = function Plane_RayTracer$Thing$get_surface() {
return this.surface;
};
$p.RayTracer$Thing$intersect = $p.intersect;
$p.RayTracer$Thing$normal = $p.normal;
});
RayTracer.Surfaces = $declare("RayTracer.Surfaces", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.cctor = function() {
$t.shiny = (function() {
var $obj$ = new RayTracer.Surface.ctor();
$obj$.diffuse = $delegate(function(pos) {
return RayTracer.Color().white;
}, System.Func$2(RayTracer.Vector, RayTracer.Color), this);
$obj$.specular = $delegate(function(pos) {
return RayTracer.Color().grey;
}, System.Func$2(RayTracer.Vector, RayTracer.Color), this);
$obj$.reflect = $delegate(function(pos) {
return 0.7;
}, System.Func$2(RayTracer.Vector, System.Double), this);
$obj$.roughness = 250;
return $obj$;
}).call(this);
$t.checkerboard = (function() {
var $obj$ = new RayTracer.Surface.ctor();
$obj$.diffuse = $delegate(function(pos) {
return ((System.Math.Floor$1(pos.z) + System.Math.Floor$1(pos.x)) % 2 != 0) ? RayTracer.Color().white : RayTracer.Color().black;
}, System.Func$2(RayTracer.Vector, RayTracer.Color), this);
$obj$.specular = $delegate(function(pos) {
return RayTracer.Color().white;
}, System.Func$2(RayTracer.Vector, RayTracer.Color), this);
$obj$.reflect = $delegate(function(pos) {
return ((System.Math.Floor$1(pos.z) + System.Math.Floor$1(pos.x)) % 2 != 0) ? 0.1 : 0.7;
}, System.Func$2(RayTracer.Vector, System.Double), this);
$obj$.roughness = 150;
return $obj$;
}).call(this);
};
});
RayTracer.RayTracer = $declare("RayTracer.RayTracer", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.cctor = function() {
$t.maxDepth = 5;
};
$t.intersections = function RayTracer_intersections(ray, scene)
{
var closest = Infinity /* Double.PositiveInfinity */;
var closestInter = null;
for (var $i = 0, $a = scene.things, $length = $a.length; $i != $length; $i++) {
var thing = $a[$i];
var inter = thing.RayTracer$Thing$intersect(ray);
if (inter != null && inter.dist < closest) {
closestInter = inter;
closest = inter.dist;
}
}
return closestInter;
};
$t.testRay = function RayTracer_testRay(ray, scene)
{
var isect = RayTracer.RayTracer.intersections(ray, scene);
return (isect != null) ? isect.dist : NaN /* Double.NaN */;
};
$t.traceRay = function RayTracer_traceRay(ray, scene, depth)
{
var isect = RayTracer.RayTracer.intersections(ray, scene);
return isect == null ? RayTracer.Color().background : RayTracer.RayTracer.shade(isect, scene,
depth);
};
$t.shade = function RayTracer_shade(isect, scene, depth)
{
var d = isect.ray.dir;
var pos = RayTracer.Vector.op_Addition(RayTracer.Vector.op_Multiply(d, isect.dist), isect.ray.start);
var normal = isect.thing.RayTracer$Thing$normal(pos);
var reflectDir = RayTracer.Vector.op_Subtraction(d, (RayTracer.Vector.op_Multiply(RayTracer.Vector.op_Multiply(normal,
2), RayTracer.Vector.dot(normal, d))));
var naturalColor = RayTracer.Color.plus(RayTracer.Color().background, RayTracer.RayTracer.getNaturalColor(isect.thing,
pos, normal, reflectDir, scene));
var reflectedColor = (depth >= 5 /* RayTracer.maxDepth */) ? RayTracer.Color().grey : RayTracer.RayTracer.getReflectionColor(isect.thing,
pos, normal, reflectDir, scene, depth);
return RayTracer.Color.plus(naturalColor, reflectedColor);
};
$t.getReflectionColor = function RayTracer_getReflectionColor(thing, pos, normal, rd, scene, depth)
{
return RayTracer.Color.scale(thing.RayTracer$Thing$get_surface().reflect(pos), RayTracer.RayTracer.traceRay((function() {
var $obj$ = new RayTracer.Ray.ctor();
$obj$.start = pos;
$obj$.dir = rd;
return $obj$;
}).call(this), scene, depth + 1));
};
$t.getNaturalColor = function RayTracer_getNaturalColor(thing, pos, norm, rd, scene)
{
var result = RayTracer.Color().defaultColor;
for (var $i = 0, $a = scene.lights, $length = $a.length; $i != $length; $i++) {
var light = $a[$i];
var ldis = RayTracer.Vector.op_Subtraction(light.pos, pos);
var livec = RayTracer.Vector.norm(ldis);
var neatIsect = RayTracer.RayTracer.testRay((function() {
var $obj$ = new RayTracer.Ray.ctor();
$obj$.start = pos;
$obj$.dir = livec;
return $obj$;
}).call(this), scene);
var isInShadow = !System.Double.IsNaN(neatIsect) && (neatIsect <= RayTracer.Vector.mag(ldis));
if (isInShadow)
continue;
var illum = RayTracer.Vector.dot(livec, norm);
var lcolor = (illum > 0) ? RayTracer.Color.scale(illum, light.color) : RayTracer.Color().defaultColor;
var specular = RayTracer.Vector.dot(livec, RayTracer.Vector.norm(rd));
var scolor = (specular > 0) ? RayTracer.Color.scale(System.Math.Pow(specular, thing.RayTracer$Thing$get_surface().roughness),
light.color) : RayTracer.Color().defaultColor;
result = RayTracer.Color.plus(result, RayTracer.Color.plus(RayTracer.Color.times(thing.RayTracer$Thing$get_surface().diffuse(pos),
lcolor), RayTracer.Color.times(thing.RayTracer$Thing$get_surface().specular(pos), scolor)));
}
return result;
};
$t.render = function RayTracer_render(scene, ctx, screenWidth, screenHeight)
{
var camera = scene.camera;
for (var y = 0; y < screenHeight; y++) {
for (var x = 0; x < screenWidth; x++) {
var recenterX = (x - (screenWidth / 2)) / 2 / screenWidth;
var recenterY = -(y - (screenHeight / 2)) / 2 / screenHeight;
var dir = RayTracer.Vector.norm(RayTracer.Vector.op_Addition(RayTracer.Vector.op_Addition(camera.forward,
(RayTracer.Vector.op_Multiply(camera.right, recenterX))), (RayTracer.Vector.op_Multiply(camera.up,
recenterY))));
var color = RayTracer.RayTracer.traceRay((function() {
var $obj$ = new RayTracer.Ray.ctor();
$obj$.start = camera.pos;
$obj$.dir = dir;
return $obj$;
}).call(this), scene, 0);
ctx.fillStyle = color.ToString();
ctx.fillRect(x, y, x + 1, y + 1);
}
}
};
});
RayTracer.Program = $declare("RayTracer.Program", System.Object, 0, $RayTracer$Assembly, function($t, $p) {
$t.Main = function Program_Main(args)
{
System.Console.WriteLine$10("Hello RayTracer!");
var defaultScene = (function() {
var $obj$ = new RayTracer.Scene.ctor();
$obj$.things = $array(RayTracer.Thing, [new RayTracer.Plane.ctor(new RayTracer.Vector.ctor$1(0,
1, 0), 0, RayTracer.Surfaces().checkerboard), new RayTracer.Sphere.ctor(new RayTracer.Vector.ctor$1(0,
1, -0.25), 1, RayTracer.Surfaces().shiny), new RayTracer.Sphere.ctor(new RayTracer.Vector.ctor$1(-1,
0.5, 1.5), 0.5, RayTracer.Surfaces().shiny)]);
$obj$.lights = $array(RayTracer.Light, [(function() {
var $obj$ = new RayTracer.Light.ctor();
$obj$.pos = new RayTracer.Vector.ctor$1(-2, 2.5, 0);
$obj$.color = new RayTracer.Color.ctor$1(0.49, 0.07, 0.07);
return $obj$;
}).call(this), (function() {
var $obj$ = new RayTracer.Light.ctor();
$obj$.pos = new RayTracer.Vector.ctor$1(1.5, 2.5, 1.5);
$obj$.color = new RayTracer.Color.ctor$1(0.07, 0.07, 0.49);
return $obj$;
}).call(this), (function() {
var $obj$ = new RayTracer.Light.ctor();
$obj$.pos = new RayTracer.Vector.ctor$1(1.5, 2.5, -1.5);
$obj$.color = new RayTracer.Color.ctor$1(0.07, 0.49, 0.071);
return $obj$;
}).call(this), (function() {
var $obj$ = new RayTracer.Light.ctor();
$obj$.pos = new RayTracer.Vector.ctor$1(0, 3.5, 0);
$obj$.color = new RayTracer.Color.ctor$1(0.21, 0.21, 0.35);
return $obj$;
}).call(this)]);
$obj$.camera = new RayTracer.Camera.ctor(new RayTracer.Vector.ctor$1(3, 2, 4), new RayTracer.Vector.ctor$1(-1,
0.5, 0));
return $obj$;
}).call(this);
var canv = document.createElement("canvas");
canv.width = 256;
canv.height = 256;
document.body.appendChild(canv);
var ctx = canv.getContext("2d");
RayTracer.RayTracer.render(defaultScene, ctx, 256, 256);
};
});