1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
import {Colour} from './Colour';
import {Framebuffer} from './Framebuffer';
import {Plane, Sphere} from './Geometry';
import {Light} from './Light';
import {Material} from './Material';
import {Raytracer, RaytracerOptions} from './Raytracer';
import {Vector} from './Vector';
function parseOptions(): RaytracerOptions {
return {
shadows: getInputElement('shadows-toggle').checked,
diffuseLighting: getInputElement('diffuse-toggle').checked,
specularLighting: getInputElement('specular-toggle').checked,
reflections: getInputElement('reflections-toggle').checked,
maxRecurseDepth: 5,
maxDrawDistance: 1000,
};
}
function initRaytracer(options: RaytracerOptions): Raytracer {
const width = 960;
const height = 720;
const fov = Math.PI / 3;
const framebuffer = new Framebuffer(width, height);
const matWhite = new Material(new Colour(102, 102, 77), 0.6, 0.3, 0.1, 50);
const matRed = new Material(new Colour(77, 26, 26), 0.9, 0.1, 0.0, 10);
const matMirror = new Material(new Colour(193, 193, 193), 0.0, 10, 0.8, 1000);
const matGreen= new Material(new Colour(77, 255, 26), 0.3, 0.1, 0.0, 2);
const spheres = [
new Sphere(new Vector(-3, 0, -16), 2, matWhite),
new Sphere(new Vector(-1, -1.5, -12), 2, matGreen),
new Sphere(new Vector(1.5, -0.5, -18), 3, matRed),
new Sphere(new Vector(7, 5, -18), 4, matMirror),
];
const planes = [
new Plane(-4, 10, -10, -30, 0.7)
];
const lights = [
new Light(new Vector(-20, 20, 20), 1.5),
new Light(new Vector(30, 50, -25), 1.8),
new Light(new Vector(30, 20, 30), 1.7),
];
return new Raytracer(framebuffer, fov, spheres, planes, lights, options);
}
function render() {
const raytracer = initRaytracer(parseOptions());
showLoadingIndicator();
// Give browser time to repaint
setTimeout(() => {
const startMs = new Date().getTime();
raytracer.render();
console.log(`Render took ${new Date().getTime() - startMs}ms`);
hideLoadingIndicator();
}, 50);
}
function registerEventListeners() {
document.getElementById('render')?.addEventListener('click', render); // TODO disable once clicked
}
function showLoadingIndicator() {
document.getElementById('output-wrapper')?.classList.add('loading');
}
function hideLoadingIndicator() {
document.getElementById('output-wrapper')?.classList.remove('loading');
}
function getInputElement(elementId: string) {
return document.getElementById(elementId) as HTMLInputElement;
}
document.addEventListener('DOMContentLoaded', () => {
registerEventListeners();
render();
});
|