From 16b0f20415efd3ed2c181df74a473ebd5265892b Mon Sep 17 00:00:00 2001 From: James Barnett Date: Tue, 4 Jan 2022 21:44:49 +0000 Subject: Fix specular lighting. Update scene. --- index.html | 56 ++++++++++++++----------- src/Geometry.ts | 17 ++++++-- src/RaytraceContext.ts | 5 +++ src/Raytracer.ts | 15 +++---- src/index.ts | 110 ++++++++++++++++++++++++++++++++----------------- style.css | 25 +++++++---- 6 files changed, 144 insertions(+), 84 deletions(-) diff --git a/index.html b/index.html index cafe649..c65728e 100644 --- a/index.html +++ b/index.html @@ -8,33 +8,26 @@ -
+
+
+
+
-
- -
-
- - -
-

Render options

+
+
+ Render options
-

Performance options

+
+
+ Performance options
-
+
+
+
+ + +
- +
diff --git a/src/Geometry.ts b/src/Geometry.ts index 41d6023..556c7ea 100644 --- a/src/Geometry.ts +++ b/src/Geometry.ts @@ -19,21 +19,30 @@ export class Sphere { } export class Plane { + @Type(() => Colour) + readonly checkerboardColour1: Colour; + @Type(() => Colour) + readonly checkerboardColour2: Colour; constructor( readonly yPos: number, readonly width: number, // How far the plane extends into x/-x from 0 readonly zStartPos: number, readonly zEndPos: number, - readonly checkerboardScale: number - ) {} + readonly checkerboardScale: number, + checkerboardColour1: Colour, + checkerboardColour2: Colour + ) { + this.checkerboardColour1 = checkerboardColour1; + this.checkerboardColour2 = checkerboardColour2; + } getMaterialAtPoint(x: number, z: number): Material { let colour: Colour; // prettier-ignore if ((Math.round(this.checkerboardScale * x) + Math.round(this.checkerboardScale * z)) & 1) { - colour = new Colour(15, 15, 15); + colour = this.checkerboardColour1; } else { - colour = new Colour(200, 200, 200); + colour = this.checkerboardColour2; } return new Material(colour, new Albedo(1, 0, 0, 0), 0, 1); } diff --git a/src/RaytraceContext.ts b/src/RaytraceContext.ts index f7c9315..acd4336 100644 --- a/src/RaytraceContext.ts +++ b/src/RaytraceContext.ts @@ -1,4 +1,5 @@ import {Type} from 'class-transformer'; +import {Colour} from './Colour'; import {Plane, Sphere} from './Geometry'; import {Light} from './Light'; @@ -9,6 +10,8 @@ export class RaytraceContext { readonly planes: Plane[]; @Type(() => Light) readonly lights: Light[]; + @Type(() => Colour) + readonly backgroundColour: Colour; constructor( readonly height: number, readonly width: number, @@ -16,11 +19,13 @@ export class RaytraceContext { spheres: Sphere[], planes: Plane[], lights: Light[], + backgroundColour: Colour, readonly options: RaytracerOptions ) { this.spheres = spheres; this.planes = planes; this.lights = lights; + this.backgroundColour = backgroundColour; } } diff --git a/src/Raytracer.ts b/src/Raytracer.ts index 895073c..34cbe00 100644 --- a/src/Raytracer.ts +++ b/src/Raytracer.ts @@ -53,7 +53,7 @@ class Raytracer { for (let x = 0; x < this.context.width; x++) { const rayX = x + 0.5 - this.context.width / 2; - const rayY = -(y + 0.5) + this.context.height / 2; + const rayY = -(y + 100) + this.context.height / 2; const rayZ = -this.context.height / (2 * Math.tan(this.context.fov / 2)); const rayDirection = new Vector(rayX, rayY, rayZ).normalise(); @@ -91,10 +91,7 @@ class Raytracer { recursionDepth > this.context.options.maxRecurseDepth || !result.geometryHit ) { - // No hit, show background colour - // return new Colour(50, 178, 203); - // return new Colour(150, 150, 150); - return new Colour(0, 128, 128); + return this.context.backgroundColour; } let reflectionColour = new Colour(0, 0, 0); @@ -218,7 +215,9 @@ class Raytracer { } }); - let rgbVector = result.material.diffuseColour.toVector(); + let rgbVector = result.material.diffuseColour + .toVector() + .multiply(result.material.albedo.diffuseAlbedo); if (this.context.options.diffuseLighting) { rgbVector = rgbVector.multiply(diffuseLightIntensity); @@ -229,9 +228,7 @@ class Raytracer { .multiply(specularLightIntensity) .multiply(result.material.albedo.specularAlbedo); - rgbVector = rgbVector - .multiply(result.material.albedo.diffuseAlbedo) - .add(totalSpecularIntensity); + rgbVector = rgbVector.add(totalSpecularIntensity); } if (this.context.options.reflections) { diff --git a/src/index.ts b/src/index.ts index a52399d..df4d988 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,50 +21,63 @@ function initDispatcher(options: RaytracerOptions): RaytraceDispatcher { const framebuffer = new Framebuffer(width, height); - const matWhite = new Material( - new Colour(102, 102, 77), - new Albedo(0.6, 0.3, 0.1, 0.0), - 50, + const materialMirror = new Material( + new Colour(220, 220, 220), + new Albedo(0.1, 1, 0.8, 0.0), + 2500, 1 ); - const matRed = new Material( - new Colour(77, 26, 26), - new Albedo(0.9, 0.1, 0.0, 0.0), - 10, - 1 - ); - const matMirror = new Material( - new Colour(193, 193, 193), - new Albedo(0.0, 10, 0.8, 0.0), - 1000, - 1 - ); - const matGreen = new Material( - new Colour(77, 255, 26), - new Albedo(0.3, 0.1, 0.0, 0.0), - 2, - 1 - ); - const matGlass = new Material( + + const materialGlass = new Material( new Colour(153, 179, 204), new Albedo(0.0, 0.5, 0.1, 0.8), 125, 1.5 ); - const spheres = [ - new Sphere(2, new Vector(-3, 0, -16), matWhite), - new Sphere(2, new Vector(-1, -1.5, -12), matGlass), - new Sphere(3, new Vector(1.5, -0.5, -18), matRed), - new Sphere(4, new Vector(7, 5, -18), matMirror), + const spheres: Sphere[] = [ + new Sphere(1, new Vector(-6.5, -3, -26), matteMaterial(27, 118, 255)), + new Sphere(1, new Vector(-14, -3, -25), matteMaterial(146, 80, 188)), + new Sphere(1, new Vector(-10, -3, -25), matteMaterial(0, 146, 178)), + new Sphere(1, new Vector(-10, -3, -20), matteMaterial(185, 18, 27)), + new Sphere(1, new Vector(-2.5, -3, -20), matteMaterial(115, 45, 217)), + new Sphere(1.5, new Vector(-10.5, -2.5, -16), materialMirror), + new Sphere(1, new Vector(-3, -3, -16), matteMaterial(247, 178, 173)), + new Sphere(1, new Vector(-6, -3, -18), matteMaterial(154, 188, 167)), + new Sphere(1, new Vector(-6, -3, -12), matteMaterial(96, 125, 139)), + new Sphere(1, new Vector(-9.5, -3, -12), matteMaterial(122, 186, 242)), + new Sphere(1, new Vector(0, -3, -14), matteMaterial(250, 91, 15)), + new Sphere(1, new Vector(-3, -3, -11), materialGlass), + new Sphere(1, new Vector(-1, -3, -10), matteMaterial(54, 95, 182)), + new Sphere(1, new Vector(-4.5, -3, -8), matteMaterial(139, 195, 74)), + + new Sphere(4, new Vector(4, 0, -18), materialMirror), + new Sphere(1, new Vector(4, -3, -12), matteMaterial(115, 45, 217)), + new Sphere(1.5, new Vector(8.5, -2.5, -10), materialMirror), + new Sphere(1, new Vector(1, -3, -11.5), matteMaterial(255, 200, 0)), + new Sphere(1, new Vector(1.2, -3, -8.2), materialGlass), + new Sphere(1, new Vector(4, -3, -7), matteMaterial(244, 67, 54)), + new Sphere(1, new Vector(5.5, -3, -9.5), matteMaterial(150, 237, 137)), + new Sphere(1, new Vector(6.5, -3, -15), matteMaterial(14, 234, 255)), + new Sphere(1, new Vector(10, -3, -16), matteMaterial(171, 71, 188)), + new Sphere(1, new Vector(11, -3, -20), matteMaterial(190, 189, 191)), ]; - const planes = [new Plane(-4, 10, -10, -30, 0.7)]; + const planes = [ + new Plane( + -4, + 50, + 40, + -45, + 1.5, + new Colour(116, 101, 87), + new Colour(92, 78, 70) + ), + ]; 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), + new Light(new Vector(30, 50, 40), 2.5), + new Light(new Vector(-20, 50, -25), 0.5), ]; const context = new RaytraceContext( @@ -74,6 +87,7 @@ function initDispatcher(options: RaytracerOptions): RaytraceDispatcher { spheres, planes, lights, + new Colour(221, 221, 221), options ); @@ -102,17 +116,19 @@ function parseOptions(): RaytracerOptions { function parseResolution(): {width: number; height: number} { switch (getInputElement('res').value) { + case '360p': + return {width: 640, height: 360}; + case '480p': + return {width: 854, height: 480}; case '720p': default: - return {width: 960, height: 720}; + return {width: 1280, height: 720}; case '1080p': - return {width: 1440, height: 1080}; + return {width: 1920, height: 1080}; case '1440p': - return {width: 1920, height: 1440}; + return {width: 2560, height: 1440}; case '4k': - return {width: 2880, height: 2160}; - case '8k': - return {width: 5760, height: 4320}; + return {width: 3840, height: 2160}; } } @@ -157,6 +173,24 @@ function getInputElement(elementId: string) { return document.getElementById(elementId) as HTMLInputElement; } +function matteMaterial(r: number, g: number, b: number): Material { + return new Material( + new Colour(r, g, b), + new Albedo(0.4, 0.0, 0.0, 0.0), + 0, + 1 + ); +} + +function glossMaterial(r: number, g: number, b: number): Material { + return new Material( + new Colour(r, g, b), + new Albedo(0.4, 0.1, 0.05, 0), + 250, + 1 + ); +} + document.addEventListener('DOMContentLoaded', () => { registerEventListeners(); render(); diff --git a/style.css b/style.css index 453687e..b4c8897 100644 --- a/style.css +++ b/style.css @@ -3,19 +3,16 @@ body { } p { - margin: 0; + margin: 0 0 0.8rem; } #output-wrapper { - position: relative; - width: 960px; - height: 720px; margin-top: 0.5em; } #render-output { - width: 960px; - height: 720px; + width: 100%; + height: 65vmin; object-fit: contain; } @@ -33,8 +30,18 @@ p { } #console { - height: 150px; - width: 300px; - padding: 0rem; + height: 120px; + padding: 0 0 0 1em; white-space: pre-line; +} + +#res-label { + padding-bottom: 4px; + padding-top: 0px; + margin-top: -1px; + font-size: 15px; +} + +.divider-vert { + padding: 0; } \ No newline at end of file -- cgit v1.2.3