/// <reference path='trace_gfx.ts'/>
/// <reference path='3d_prim.ts'/>
/// <reference path='math_3d.ts'/>
/// <reference path='lighting.ts'/>
let canvas : any = document.getElementById("drawCanvas");
let g : Graphics = new Graphics(canvas, 256, 256);
let sphereArray : Sphere[] = [
new Sphere(new Vector3(-4 * Math.random() + 2, 1 * Math.random(), 4 * Math.random() + 1), 1, new Vector3(255, 255, 0), 500),
new Sphere(new Vector3(-4 * Math.random() + 2, 1 * Math.random(), 4 * Math.random() + 1), 1, new Vector3(255, 0, 255), 500),
new Sphere(new Vector3(-4 * Math.random() + 2, 1 * Math.random(), 4 * Math.random() + 1), 1, new Vector3(0, 255, 255), 10),
new Sphere(new Vector3(0, -5001, 0), 5000, new Vector3(230, 230, 230), 1000),
];
let lightArray : Light[] = [
new Light(LIGHT_TYPE.AMBIENT, 0.2),
new Light(LIGHT_TYPE.POINT, 0.6, new Vector3(2, 1, 0)),
new Light(LIGHT_TYPE.DIRECTIONAL, 0.2, undefined, new Vector3(1, 4, 4))
];
let camera : Vector3 = new Vector3(0, 0, 0);
for(let x : number = -g.w / 2;x < g.w / 2;x++) {
for(let y : number = -g.h / 2;y < g.h / 2;y++) {
let hit : Ray_Result = Math_3D.IntersectAllSpheres(camera, new Vector3(x / g.w, -y / g.h, 1), sphereArray);
if(hit.sphere != undefined) {
let ray_dir = Math_3D.UnitVector(new Vector3(x / g.w, -y / g.h, 1));
let light_index : number = Lighting.CalcLighting(hit.hit_point, hit.hit_normal, new Vector3(-ray_dir.x, -ray_dir.y, -ray_dir.z), lightArray, hit.sphere.specular, sphereArray);
g.putPixel(new Vector2(x + g.w / 2, y + g.w / 2), new Vector3(hit.sphere.color.x * light_index, hit.sphere.color.y * light_index, hit.sphere.color.z * light_index));
} else {
g.putPixel(new Vector2(x + g.w / 2, y + g.w / 2), new Vector3(0, 0, 255));
}
}
}
g.showBuffer();
/// <reference path='trace_gfx.ts'/>
class Sphere {
constructor(
public center : Vector3,
public radius : number,
public color : Vector3,
public specular : number
) {}
}
/// <reference path='trace_gfx.ts'/>
/// <reference path='math_3d.ts'/>
enum LIGHT_TYPE {
AMBIENT,
POINT,
DIRECTIONAL
}
class Light {
constructor(
public type : LIGHT_TYPE,
public intensity : number,
public position? : Vector3,
public direction? : Vector3
) {}
}
class Lighting {
public static CalcLighting(P : Vector3, N : Vector3, V : Vector3, lights : Light[], s : number, sphereArray : Sphere[]) : number {
let i : number = 0;
for(let l : number = 0;l < lights.length;l++) {
let li : Light = lights[l];
if(li.type == LIGHT_TYPE.AMBIENT) {
i += li.intensity;
} else {
let L : Vector3 = undefined;
if(li.type == LIGHT_TYPE.POINT) {
L = Math_3D.SubtractVectors(li.position, P);
} else {
L = li.direction;
}
let new_l : Vector3 = Math_3D.AddVectors(L, N);
let hit : Ray_Result = Math_3D.IntersectAllSpheres(P, new_l, sphereArray);
if(hit.sphere != undefined) {
continue;
}
let n_dot_l : number = Math_3D.DotProduct(N, L);
if(n_dot_l > 0) {
i += li.intensity * n_dot_l / (Math_3D.VectorMagnitude(N) * Math_3D.VectorMagnitude(L));
}
if(s != -1) {
let R1 : Vector3 = Math_3D.MultiplyVector(N, 2);
let R2 : number = Math_3D.DotProduct(N, L);
let R3 : Vector3 = Math_3D.MultiplyVector(R1, R2);
let R4 : Vector3 = Math_3D.SubtractVectors(R3, L);
let r_dot_v : number = Math_3D.DotProduct(R4, V);
if(r_dot_v > 0) {
i += li.intensity * Math.pow(r_dot_v / (Math_3D.VectorMagnitude(R4) * Math_3D.VectorMagnitude(V)), s);
}
}
}
}
return i;
}
}
/// <reference path='trace_gfx.ts'/>
/// <reference path='3d_prim.ts'/>
interface Ray_Result {
sphere : Sphere;
distance : number;
hit_point : Vector3;
hit_normal : Vector3;
}
class Math_3D {
public static AddVectors(V1 : Vector3, V2 : Vector3) : Vector3 {
let a1 : number[] = [V1.x, V1.y, V1.z];
let a2 : number[] = [V2.x, V2.y, V2.z];
let result : number[] = a1.map(function(item, index) {
return item + a2[index];
});
return new Vector3(result[0], result[1], result[2]);
}
public static SubtractVectors(V1 : Vector3, V2 : Vector3) : Vector3 {
let a1 : number[] = [V1.x, V1.y, V1.z];
let a2 : number[] = [V2.x, V2.y, V2.z];
let result : number[] = a1.map(function(item, index) {
return item - a2[index];
});
return new Vector3(result[0], result[1], result[2]);
}
public static DotProduct(V1 : Vector3, V2 : Vector3) : number {
return V1.x * V2.x + V1.y * V2.y + V1.z * V2.z;
}
public static CrossProduct(V1 : Vector3, V2 : Vector3) : Vector3 {
let cx : number = V1.y * V2.z - V1.z * V2.y;
let cy : number = V1.z * V2.x - V1.x * V2.z;
let cz : number = V1.x * V2.y - V1.y * V2.x;
return new Vector3(cx, cy, cz);
}
public static VectorMagnitude(V : Vector3) : number {
return Math.sqrt(V.x * V.x + V.y * V.y + V.z * V.z);
}
public static MultiplyVector(V : Vector3, N : number) : Vector3 {
return new Vector3(V.x * N, V.y * N, V.z * N);
}
public static DivideVector(V : Vector3, N : number) : Vector3 {
return new Vector3(V.x / N, V.y / N, V.z / N);
}
public static UnitVector(V : Vector3) : Vector3 {
return Math_3D.DivideVector(V, Math_3D.VectorMagnitude(V));
}
public static AbsVector(V : Vector3) : Vector3 {
return new Vector3(Math.abs(V.x), Math.abs(V.y), Math.abs(V.z));
}
public static IntersectRaySphere(o : Vector3, lnn : Vector3, sphere : Sphere) : number {
let l : Vector3 = Math_3D.UnitVector(lnn);
let c : Vector3 = sphere.center;
let r : number = sphere.radius;
let oc : Vector3 = Math_3D.SubtractVectors(o, c);
let ld : number = Math_3D.DotProduct(l, oc);
let ldn : number = -ld;
let lds : number = ld * ld;
let mag : number = Math_3D.VectorMagnitude(oc);
mag = mag * mag;
let result : number = lds - mag + r * r;
let sq : number = Math.sqrt(result);
let r1 : number = ldn - sq;
let r2 : number = ldn + sq;
if(r1 < r2) {
return r1;
} else {
return r2;
}
}
public static IntersectAllSpheres(o : Vector3, lnn : Vector3, sphereArray : Sphere[]) : Ray_Result {
let l : Vector3 = Math_3D.UnitVector(lnn);
let sphere : Sphere = undefined;
let distance : number = 999;
for(let i : number = 0;i < sphereArray.length;i++) {
let t_dist : number = Math_3D.IntersectRaySphere(o, lnn, sphereArray[i]);
if(distance > t_dist && t_dist >= 0) {
distance = t_dist;
sphere = sphereArray[i];
}
}
if(sphere != undefined) {
let P : Vector3 = Math_3D.AddVectors(o, Math_3D.MultiplyVector(l, distance));
let N : Vector3 = Math_3D.SubtractVectors(P, sphere.center)
let NORM : Vector3 = Math_3D.UnitVector(N);
return {sphere: sphere, distance: distance, hit_point: P, hit_normal: NORM};
}
return {sphere: sphere, distance: distance, hit_point: undefined, hit_normal: undefined};
}
}
class Vector2 {
constructor(
public x : number,
public y : number
) {}
}
class Vector3 {
constructor(
public x : number,
public y : number,
public z : number
) {}
}
class Graphics {
private ctx : any;
private buf8 : any;
private imageData : any;
constructor(canvas : any, public w : number, public h : number) {
canvas.width = w;
canvas.height = h;
this.ctx = canvas.getContext("2d");
this.imageData = this.ctx.getImageData(0, 0, w, h);
let buf : any = new ArrayBuffer(this.imageData.data.length);
this.buf8 = new Uint8ClampedArray(buf);
}
putPixel(pos : Vector2, color : Vector3) : void {
let offset : number = (pos.y * this.w + pos.x) * 4;
this.buf8[offset + 3] = 255;
this.buf8[offset] = color.x;
this.buf8[offset + 1] = color.y;
this.buf8[offset + 2] = color.z;
}
showBuffer() : void {
this.imageData.data.set(this.buf8);
this.ctx.putImageData(this.imageData, 0, 0);
}
}