aboutsummaryrefslogtreecommitdiff
path: root/src/Raytracer.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/Raytracer.ts')
-rw-r--r--src/Raytracer.ts93
1 files changed, 83 insertions, 10 deletions
diff --git a/src/Raytracer.ts b/src/Raytracer.ts
index 673977b..895073c 100644
--- a/src/Raytracer.ts
+++ b/src/Raytracer.ts
@@ -1,6 +1,6 @@
import {Colour} from './Colour';
import {Plane, Sphere} from './Geometry';
-import {Material} from './Material';
+import {Albedo, Material} from './Material';
import {RaytraceContext} from './RaytraceContext';
import {Vector} from './Vector';
@@ -99,7 +99,7 @@ class Raytracer {
let reflectionColour = new Colour(0, 0, 0);
if (this.context.options.reflections) {
- const reflectionDirection = this.calculateReflection(
+ const reflectionDirection = this.calculateReflectionVector(
ray.direction,
result.normal
);
@@ -117,14 +117,45 @@ class Raytracer {
);
}
- return this.processLighting(result, ray.direction, reflectionColour);
+ let refractionColour = new Colour(0, 0, 0);
+ if (this.context.options.refractions) {
+ const refractionDirection = this.calculateRefractionVector(
+ ray.direction,
+ result.normal,
+ result.material.refractiveIndex
+ );
+ let refractionOrigin: Vector;
+ if (refractionDirection.dotProduct(result.normal) < 0) {
+ refractionOrigin = result.hitPoint.subtract(
+ result.normal.multiply(0.001)
+ );
+ } else {
+ refractionOrigin = result.hitPoint.add(result.normal.multiply(0.001));
+ }
+ refractionColour = this.raytrace(
+ new Ray(refractionOrigin, refractionDirection),
+ ++recursionDepth
+ );
+ }
+
+ return this.processLighting(
+ result,
+ ray.direction,
+ reflectionColour,
+ refractionColour
+ );
}
private processSceneGeometry(ray: Ray): RayTraceResult {
let geometryDistance = 99999;
let hitPoint = new Vector(0, 0, 0);
let normal = new Vector(0, 0, 0);
- let material = new Material(new Colour(0, 0, 0), 0, 0, 0, 0);
+ let material = new Material(
+ new Colour(0, 0, 0),
+ new Albedo(0, 0, 0, 0),
+ 0,
+ 0
+ );
this.context.spheres.forEach(sphere => {
const result = this.intersectSphere(ray, sphere);
@@ -156,7 +187,8 @@ class Raytracer {
private processLighting(
result: RayTraceResult,
direction: Vector,
- reflectionColour: Colour
+ reflectionColour: Colour,
+ refractionColour: Colour
): Colour {
let diffuseLightIntensity = 0;
let specularLightIntensity = 0;
@@ -176,7 +208,7 @@ class Raytracer {
Math.pow(
Math.max(
0,
- this.calculateReflection(
+ this.calculateReflectionVector(
lightDirection,
result.normal
).dotProduct(direction)
@@ -195,23 +227,29 @@ class Raytracer {
if (this.context.options.specularLighting) {
const totalSpecularIntensity = new Vector(255, 255, 255)
.multiply(specularLightIntensity)
- .multiply(result.material.specularAlbedo);
+ .multiply(result.material.albedo.specularAlbedo);
rgbVector = rgbVector
- .multiply(result.material.diffuseAlbedo)
+ .multiply(result.material.albedo.diffuseAlbedo)
.add(totalSpecularIntensity);
}
if (this.context.options.reflections) {
rgbVector = rgbVector.add(
- reflectionColour.multiply(result.material.reflectionAlbedo)
+ reflectionColour.multiply(result.material.albedo.reflectionAlbedo)
+ );
+ }
+
+ if (this.context.options.refractions) {
+ rgbVector = rgbVector.add(
+ refractionColour.multiply(result.material.albedo.refractionAlbedo)
);
}
return Colour.fromVector(rgbVector);
}
- private calculateReflection(
+ private calculateReflectionVector(
incidentAngle: Vector,
surfaceNormal: Vector
): Vector {
@@ -223,6 +261,41 @@ class Raytracer {
);
}
+ private calculateRefractionVector(
+ incidentAngle: Vector,
+ surfaceNormal: Vector,
+ refractiveIndex: number
+ ): Vector {
+ // See https://en.wikipedia.org/wiki/Snell's_law#Vector_form
+ let cosIncidenceAngle = -Math.max(
+ -1,
+ Math.min(1, incidentAngle.dotProduct(surfaceNormal))
+ );
+ let n1 = 1;
+ let n2 = refractiveIndex;
+
+ let normal = surfaceNormal;
+ if (cosIncidenceAngle < 0) {
+ cosIncidenceAngle = -cosIncidenceAngle;
+ n1 = refractiveIndex;
+ n2 = 1;
+ normal = surfaceNormal.negative();
+ }
+
+ const r = n1 / n2;
+ const cosRefractedAngle = Math.sqrt(
+ 1 - r * r * (1 - cosIncidenceAngle * cosIncidenceAngle)
+ );
+
+ if (cosRefractedAngle * cosRefractedAngle < 0) {
+ return new Vector(0, 0, 0);
+ } else {
+ return incidentAngle
+ .multiply(r)
+ .add(normal.multiply(r * cosIncidenceAngle - cosRefractedAngle));
+ }
+ }
+
private isShadowCast(
lightDirection: Vector,
lightDistance: number,