Now with NGINX?

Ryan DeYong's Programming Portfolio

Home In-browser Examples Bash Scripts Chrome Userscripts The Best Calculators Physical Projects Fine Art Website Source Code


This is a ray-tracing algorithm written in TypeScript that draws several spheres with some diffuse lighting and shadows.

Check the checkbox to show the code.
// trace.ts /// <reference path='trace_gfx.ts'/> /// <reference path='3d_prim.ts'/> /// <reference path='math_3d.ts'/> /// <reference path='lighting.ts'/> // create the graphics context let canvas : any = document.getElementById("drawCanvas"); let g : Graphics = new Graphics(canvas, 256, 256); // define the spheres 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), ]; // define the scene lights 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)) ]; // define camera pos and other stuff let camera : Vector3 = new Vector3(0, 0, 0); // camera is at the origin 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)); } //console.log(); } } g.showBuffer(); //console.log(g.w); // 3d_prim.ts /// <reference path='trace_gfx.ts'/> /* * Holds 3D object primitives */ /* * Structure to hold a sphere */ class Sphere { constructor( public center : Vector3, public radius : number, public color : Vector3, public specular : number ) {} } // lighting.ts /// <reference path='trace_gfx.ts'/> /// <reference path='math_3d.ts'/> /* * Holds lighting information */ /* * enum to hold light types */ enum LIGHT_TYPE { AMBIENT, POINT, DIRECTIONAL } /* * Structure to hold lights */ 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); // shadows let hit : Ray_Result = Math_3D.IntersectAllSpheres(P, new_l, sphereArray); if(hit.sphere != undefined) { continue; } // diffuse lighting 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)); } // specular lighting 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; } } // math_3d.ts /// <reference path='trace_gfx.ts'/> /// <reference path='3d_prim.ts'/> /* * Holds the functions for doing 3D math */ interface Ray_Result { sphere : Sphere; distance : number; hit_point : Vector3; hit_normal : Vector3; } class Math_3D { /* * Add 2 3D vectors * Returns a Vector3 result */ 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]); } /* * Subtract 2 3D vectors * Returns a Vector3 result */ 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]); } /* * Gets the dot product of 2 3D vectors * Returns a scalar result */ public static DotProduct(V1 : Vector3, V2 : Vector3) : number { return V1.x * V2.x + V1.y * V2.y + V1.z * V2.z; } /* * Gets the cross product of 2 3D vectors * Returns a Vector3 result */ 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); } /* * Gets the magnitude of a 3D vector * Returns a scalar result */ public static VectorMagnitude(V : Vector3) : number { return Math.sqrt(V.x * V.x + V.y * V.y + V.z * V.z); } /* * Gets the result of a vector multiplied by a number * Returns a Vector3 result */ public static MultiplyVector(V : Vector3, N : number) : Vector3 { return new Vector3(V.x * N, V.y * N, V.z * N); } /* * Gets the result of a vector divided by a number * Returns a Vector3 result */ public static DivideVector(V : Vector3, N : number) : Vector3 { return new Vector3(V.x / N, V.y / N, V.z / N); } /* * Gets the unit vector that represents a 3D vector * Returns a Vector3 result */ public static UnitVector(V : Vector3) : Vector3 { return Math_3D.DivideVector(V, Math_3D.VectorMagnitude(V)); } /* * Returns the absolute value of a vector */ public static AbsVector(V : Vector3) : Vector3 { return new Vector3(Math.abs(V.x), Math.abs(V.y), Math.abs(V.z)); } /* * Cast a ray to attempt to intersect with a sphere from start to end */ public static IntersectRaySphere(o : Vector3, lnn : Vector3, sphere : Sphere) : number { let l : Vector3 = Math_3D.UnitVector(lnn); // normalized ray direction let c : Vector3 = sphere.center; let r : number = sphere.radius; // part 1 // -(l DOT (o - c)) let oc : Vector3 = Math_3D.SubtractVectors(o, c); // o - c let ld : number = Math_3D.DotProduct(l, oc); // l DOT (o - c) let ldn : number = -ld; // -(l DOT (o - c)) // part 2 // number under radical let lds : number = ld * ld; // ld^2 let mag : number = Math_3D.VectorMagnitude(oc); // || o - c || 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; } } /* * Check all passed spheres for collision */ public static IntersectAllSpheres(o : Vector3, lnn : Vector3, sphereArray : Sphere[]) : Ray_Result { let l : Vector3 = Math_3D.UnitVector(lnn); // normalized ray direction // holds ending information 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)); // compute hit pos let N : Vector3 = Math_3D.SubtractVectors(P, sphere.center) // compute sphere normal let NORM : Vector3 = Math_3D.UnitVector(N); // gets the normalized normal return {sphere: sphere, distance: distance, hit_point: P, hit_normal: NORM}; } return {sphere: sphere, distance: distance, hit_point: undefined, hit_normal: undefined}; } } // trace_gfx.ts /* * General purpose graphics library */ /* * Structure to hold a 2D vector */ class Vector2 { constructor( public x : number, public y : number ) {} } /* * Structure to hold a 3D vector */ class Vector3 { constructor( public x : number, public y : number, public z : number ) {} } /* * Class that controls graphical output */ 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); // get the image data in the canvas // now put the bytes into an off-screen buffer let buf : any = new ArrayBuffer(this.imageData.data.length); this.buf8 = new Uint8ClampedArray(buf); } /* * Put a pixel into our int buffer */ putPixel(pos : Vector2, color : Vector3) : void { let offset : number = (pos.y * this.w + pos.x) * 4; // offset for pixels, 32-bit RGBA this.buf8[offset + 3] = 255; // alpha this.buf8[offset] = color.x; // red this.buf8[offset + 1] = color.y; // green this.buf8[offset + 2] = color.z; // blue } /* * Show the graphics buffer after drawing */ showBuffer() : void { this.imageData.data.set(this.buf8); this.ctx.putImageData(this.imageData, 0, 0); } }

About me
Contact Information:
Voicemail box #: +14408478142
Email: administrator@ryancdeyong.us

All software on this website uses the MIT License.
ξ This page was generated Tue May 06, 2025 13:27