-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmaterial.js
113 lines (109 loc) · 3.12 KB
/
material.js
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Vec3, dot, normRandDisk, randCosHemisphere, reflect, reflectance, refract } from "./vector.js";
import { HitInfo } from "./hittable.js";
import { Basis } from "./basis.js";
import { Ray } from "./ray.js";
export class Material {
/**
* Scatters an incident ray off of the material, returning the scattered ray.
* @param {Ray} ray
* @param {HitInfo} hit
* @returns {Ray}
*/
scatter(ray, hit) {}
/**
* Returns the attenuation of the material.
* @returns {Vec3}
*/
attenuation() {}
}
export class Lambertian extends Material {
/**
* Creates a new `Lambertian` material instance.
* @param {Vec3} albedo
* @returns {Lambertian}
*/
constructor(albedo) {
super();
this.albedo = albedo;
}
albedo = new Vec3;
/**
* Scatters an incident ray off of the material, returning the scattered ray.
* @param {Ray} ray
* @param {HitInfo} hit
* @returns {Ray}
*/
scatter(ray, hit) {
const tbn = new Basis(hit.normal);
const direction = tbn.localize(randCosHemisphere());
return new Ray(hit.position, direction);
}
/**
* Returns the attenuation of the material.
* @returns {Vec3}
*/
attenuation() {return this.albedo;}
}
export class Metal extends Material {
/**
* Creates a new `Metal` material instance.
* @param {Vec3} albedo
* @param {Number | undefined} fuzz
* @returns {Metal}
*/
constructor(albedo, fuzz) {
super();
this.albedo = albedo;
this.fuzz = fuzz ?? 0;
}
albedo = new Vec3;
fuzz = 0;
/**
* Scatters an incident ray off of the material, returning the scattered ray.
* @param {Ray} ray
* @param {HitInfo} hit
* @returns {Ray}
*/
scatter(ray, hit) {
const tbn = new Basis(reflect(ray.direction, hit.normal));
const direction = tbn.localize(normRandDisk(this.fuzz));
return new Ray(hit.position, direction);
}
/**
* Returns the attenuation of the material.
* @returns {Vec3}
*/
attenuation() {return this.albedo;}
}
export class Dielectric extends Material {
/**
* Creates a new `Dielectric` material instance.
* @param {Number} ir
* @returns {Dielectric}
*/
constructor(ir) {
super();
this.ir = ir;
}
ir = 1;
/**
* Scatters an incident ray off of the material, returning the scattered ray.
* @param {Ray} ray
* @param {HitInfo} hit
* @returns {Ray}
*/
scatter(ray, hit) {
const cost = -dot(ray.direction, hit.normal);
const eta = hit.isFrontFace ? 1 / this.ir : this.ir;
let direction;
const TIR = 1 - 1 / (eta * eta) > cost * cost;
if (!TIR && reflectance(ray.direction, hit.normal, eta) < Math.random()) direction = refract(ray.direction, hit.normal, eta);
else direction = reflect(ray.direction, hit.normal);
return new Ray(hit.position, direction);
}
/**
* Returns the attenuation of the material.
* @returns {Vec3}
*/
attenuation() {return new Vec3(1, 1, 1);}
}