tela.js 1.0.5 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -30
- package/package.json +3 -5
- package/src/Camera/Camera.js +55 -26
- package/src/Camera/parallel.js +77 -0
- package/src/Camera/raster.js +8 -5
- package/src/Camera/{raytrace.js → rayTrace.js} +14 -13
- package/src/Camera/rayTraceWorker.js +59 -0
- package/src/Color/Color.js +1 -1
- package/src/Geometry/Box.js +8 -0
- package/src/Geometry/Geometry.js +16 -0
- package/src/Geometry/Line.js +19 -2
- package/src/Geometry/Mesh.js +13 -4
- package/src/Geometry/Path.js +3 -0
- package/src/Geometry/{Point.js → Sphere.js} +39 -8
- package/src/Geometry/Triangle.js +31 -2
- package/src/IO/IO.js +21 -37
- package/src/Material/Material.js +18 -1
- package/src/Scene/BScene.js +9 -23
- package/src/Scene/KScene.js +11 -12
- package/src/Scene/NaiveScene.js +17 -2
- package/src/Scene/RandomScene.js +3 -4
- package/src/Scene/VoxelScene.js +32 -29
- package/src/Scene/utils.js +27 -0
- package/src/Tela/Canvas.js +76 -236
- package/src/Tela/Image.js +88 -187
- package/src/Tela/Tela.js +365 -0
- package/src/Tela/Window.js +111 -190
- package/src/Tela/utils.js +17 -0
- package/src/Utils/Constants.js +4 -31
- package/src/Utils/Math.js +1 -122
- package/src/Utils/Stream.js +7 -5
- package/src/Utils/Utils.js +39 -11
- package/src/Utils/Utils3D.js +21 -3
- package/src/Vector/Vector.js +2 -3
- package/src/index.js +5 -9
- package/src/index.node.js +2 -3
- package/dist/node/index.js +0 -5038
- package/dist/web/index.js +0 -4575
- package/src/Scene/Scene.js +0 -402
- package/src/Utils/Animation.js +0 -88
package/README.md
CHANGED
|
@@ -21,37 +21,31 @@ Playground usage:
|
|
|
21
21
|
```html
|
|
22
22
|
<!DOCTYPE html>
|
|
23
23
|
<html lang="en">
|
|
24
|
-
|
|
25
24
|
<head>
|
|
26
25
|
</head>
|
|
27
|
-
|
|
28
26
|
<body>
|
|
29
|
-
|
|
30
27
|
</body>
|
|
31
28
|
<script type="module">
|
|
32
|
-
import { Canvas,
|
|
29
|
+
import { Canvas, Color, loop } from "https://cdn.jsdelivr.net/npm/tela.js/src/index.js";
|
|
33
30
|
|
|
34
31
|
// You can also import from local file
|
|
35
|
-
// import { Canvas,
|
|
32
|
+
// import { Canvas, Color, loop } from "./node_modules/tela.js/src/index.js";
|
|
36
33
|
|
|
37
34
|
const width = 640;
|
|
38
35
|
const height = 480;
|
|
39
36
|
const canvas = Canvas.ofSize(640, 480);
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
})
|
|
37
|
+
loop(({ time, dt }) => {
|
|
38
|
+
document.title = `FPS: ${(Math.floor(1 / dt))}`;
|
|
39
|
+
canvas.map((x, y) => {
|
|
40
|
+
return Color.ofRGB(
|
|
41
|
+
((x * time) / width) % 1,
|
|
42
|
+
((y * time) / height) % 1
|
|
43
|
+
)
|
|
49
44
|
})
|
|
50
|
-
|
|
45
|
+
}).play();
|
|
51
46
|
document.body.appendChild(canvas.DOM);
|
|
52
47
|
|
|
53
48
|
</script>
|
|
54
|
-
|
|
55
49
|
</html>
|
|
56
50
|
```
|
|
57
51
|
|
|
@@ -59,23 +53,21 @@ Playground usage:
|
|
|
59
53
|
Install `tela.js` it using `npm install tela.js` / `bun add tela.js`.
|
|
60
54
|
|
|
61
55
|
```js
|
|
62
|
-
import {
|
|
63
|
-
import Window from "tela.js/src/Tela/Window.js";
|
|
56
|
+
import { loop, Color, Window} from "tela.js/src/index.node.js";
|
|
64
57
|
|
|
65
58
|
const width = 640;
|
|
66
59
|
const height = 480;
|
|
67
60
|
const window = Window.ofSize(640, 480);
|
|
68
|
-
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
})
|
|
61
|
+
loop(({ time, dt }) => {
|
|
62
|
+
window.setTitle(`FPS: ${Math.floor(1 / dt)}`);
|
|
63
|
+
window.map((x, y) => {
|
|
64
|
+
return Color.ofRGB(
|
|
65
|
+
((x * time) / width) % 1,
|
|
66
|
+
((y * time) / height) % 1
|
|
67
|
+
)
|
|
77
68
|
})
|
|
78
|
-
|
|
69
|
+
})
|
|
70
|
+
.play();
|
|
79
71
|
```
|
|
80
72
|
|
|
81
73
|
And run it: `node index.mjs` / `bun index.js`
|
|
@@ -90,7 +82,7 @@ Install `tela.js` it using `npm install tela.js` / `bun add tela.js`.
|
|
|
90
82
|
Create a file:
|
|
91
83
|
```js
|
|
92
84
|
// index.js
|
|
93
|
-
import { Color, video } from "tela.js/
|
|
85
|
+
import { Color, video } from "tela.js/src/index.node.js";
|
|
94
86
|
|
|
95
87
|
const width = 640;
|
|
96
88
|
const height = 480;
|
|
@@ -111,7 +103,7 @@ video(
|
|
|
111
103
|
animation,
|
|
112
104
|
{ width, height, FPS }
|
|
113
105
|
)
|
|
114
|
-
.
|
|
106
|
+
.while(({ time }) => time < maxVideoTime);
|
|
115
107
|
```
|
|
116
108
|
|
|
117
109
|
And run it: `node index.mjs` / `bun index.js`
|
|
@@ -143,6 +135,13 @@ You can find more examples of usage in:
|
|
|
143
135
|
[Node][node] is preferred when running the demos (it is faster, [opened a bug in bun](https://github.com/oven-sh/bun/issues/9218)), [bun][bun] is needed to build the library.
|
|
144
136
|
|
|
145
137
|
|
|
138
|
+
# TODOs
|
|
139
|
+
|
|
140
|
+
- Fix parallel ray trace refresh bug
|
|
141
|
+
- Parallel ray map
|
|
142
|
+
- Serialize meshes not only triangles
|
|
143
|
+
- Optimize data serialization in parallel ray tracer
|
|
144
|
+
|
|
146
145
|
|
|
147
146
|
[ffmpeg]: https://ffmpeg.org/
|
|
148
147
|
[bun]: https://bun.sh/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tela.js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"author": "Pedroth",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,11 +37,9 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "Apache-2.0",
|
|
39
39
|
"scripts": {
|
|
40
|
-
"build": "bun run clean; bun i; bun bundle.js",
|
|
41
|
-
"buildDev": "bun bundle.js --watch",
|
|
42
40
|
"serve": "bunx http-server",
|
|
43
|
-
"clean": "rm -fr node_modules; rm
|
|
44
|
-
"pub": "npm version patch;
|
|
41
|
+
"clean": "rm -fr node_modules; rm *.png *.jpeg *.ppm *.webm *.mp4",
|
|
42
|
+
"pub": "npm version patch; npm publish"
|
|
45
43
|
},
|
|
46
44
|
"type": "module"
|
|
47
45
|
}
|
package/src/Camera/Camera.js
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
import { Vec2, Vec3 } from "../Vector/Vector.js"
|
|
1
|
+
import Vec, { Vec2, Vec3 } from "../Vector/Vector.js"
|
|
2
2
|
import Ray from "../Ray/Ray.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getRayTracer } from "./rayTrace.js";
|
|
4
4
|
import { rasterGraphics } from "./raster.js";
|
|
5
5
|
import { sdfTrace } from "./sdf.js";
|
|
6
6
|
import { normalTrace } from "./normal.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { parallelWorkers } from "./parallel.js";
|
|
9
8
|
|
|
10
9
|
export default class Camera {
|
|
11
10
|
constructor(props = {}) {
|
|
12
|
-
const { lookAt, distanceToPlane, position } = props;
|
|
11
|
+
const { lookAt, distanceToPlane, position, orientCoords, orbitCoords } = props;
|
|
13
12
|
this.lookAt = lookAt ?? Vec3(0, 0, 0);
|
|
14
13
|
this.distanceToPlane = distanceToPlane ?? 1;
|
|
15
14
|
this.position = position ?? Vec3(3, 0, 0);
|
|
16
|
-
this._orientCoords = Vec2();
|
|
17
|
-
this._orbitCoords =
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
clone() {
|
|
22
|
-
return new Camera({
|
|
23
|
-
lookAt: this.lookAt,
|
|
24
|
-
position: this.position,
|
|
25
|
-
distanceToPlane: this.distanceToPlane,
|
|
26
|
-
})
|
|
15
|
+
this._orientCoords = orientCoords ?? Vec2();
|
|
16
|
+
this._orbitCoords = orbitCoords;
|
|
17
|
+
if (this._orbitCoords) this.orbit(...this._orbitCoords.toArray());
|
|
18
|
+
else this.orient(...this._orientCoords.toArray());
|
|
19
|
+
this._orbitCoords = this._orbitCoords ?? Vec3(this.position.length());
|
|
27
20
|
}
|
|
28
21
|
|
|
29
22
|
look(at, up = Vec3(0, 0, 1)) {
|
|
@@ -91,11 +84,13 @@ export default class Camera {
|
|
|
91
84
|
return {
|
|
92
85
|
to: canvas => {
|
|
93
86
|
const w = canvas.width;
|
|
87
|
+
const invW = 1 / w;
|
|
94
88
|
const h = canvas.height;
|
|
89
|
+
const invH = 1 / h;
|
|
95
90
|
const ans = canvas.map((x, y) => {
|
|
96
91
|
const dirInLocal = [
|
|
97
|
-
(x
|
|
98
|
-
(y
|
|
92
|
+
(x * invW - 0.5),
|
|
93
|
+
(y * invH - 0.5),
|
|
99
94
|
this.distanceToPlane
|
|
100
95
|
]
|
|
101
96
|
const dir = Vec3(
|
|
@@ -112,11 +107,11 @@ export default class Camera {
|
|
|
112
107
|
}
|
|
113
108
|
}
|
|
114
109
|
|
|
115
|
-
sceneShot(scene, params
|
|
116
|
-
return this.rayMap(
|
|
110
|
+
sceneShot(scene, params) {
|
|
111
|
+
return this.rayMap(getRayTracer(scene, params));
|
|
117
112
|
}
|
|
118
113
|
|
|
119
|
-
reverseShot(scene, params
|
|
114
|
+
reverseShot(scene, params) {
|
|
120
115
|
return {
|
|
121
116
|
to: rasterGraphics(scene, this, params)
|
|
122
117
|
}
|
|
@@ -130,6 +125,18 @@ export default class Camera {
|
|
|
130
125
|
return this.rayMap(normalTrace(scene));
|
|
131
126
|
}
|
|
132
127
|
|
|
128
|
+
parallelShot(scene, params) {
|
|
129
|
+
return {
|
|
130
|
+
to: canvas => {
|
|
131
|
+
return Promise
|
|
132
|
+
.all(parallelWorkers(this, scene, canvas, params))
|
|
133
|
+
.then(() => {
|
|
134
|
+
canvas.paint();
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
133
140
|
toCameraCoord(x) {
|
|
134
141
|
let pointInCamCoord = x.sub(this.position);
|
|
135
142
|
pointInCamCoord = Vec3(
|
|
@@ -148,13 +155,15 @@ export default class Camera {
|
|
|
148
155
|
return x;
|
|
149
156
|
}
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
const w =
|
|
153
|
-
const
|
|
158
|
+
rayFromImage(width, height) {
|
|
159
|
+
const w = width;
|
|
160
|
+
const invW = 1 / w;
|
|
161
|
+
const h = height;
|
|
162
|
+
const invH = 1 / h;
|
|
154
163
|
return (x, y) => {
|
|
155
164
|
const dirInLocal = [
|
|
156
|
-
(x
|
|
157
|
-
(y
|
|
165
|
+
(x * invW - 0.5),
|
|
166
|
+
(y * invH - 0.5),
|
|
158
167
|
this.distanceToPlane
|
|
159
168
|
]
|
|
160
169
|
const dir = Vec3(
|
|
@@ -166,4 +175,24 @@ export default class Camera {
|
|
|
166
175
|
return Ray(this.position, dir);
|
|
167
176
|
}
|
|
168
177
|
}
|
|
178
|
+
|
|
179
|
+
serialize() {
|
|
180
|
+
return {
|
|
181
|
+
lookAt: this.lookAt.toArray(),
|
|
182
|
+
distanceToPlane: this.distanceToPlane,
|
|
183
|
+
position: this.position.toArray(),
|
|
184
|
+
orientCoords: this._orientCoords.toArray(),
|
|
185
|
+
orbitCoords: this._orbitCoords.toArray(),
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static deserialize(json) {
|
|
190
|
+
return new Camera({
|
|
191
|
+
lookAt: Vec.fromArray(json.lookAt),
|
|
192
|
+
distanceToPlane: json.distanceToPlane,
|
|
193
|
+
position: Vec.fromArray(json.position),
|
|
194
|
+
orientCoords: Vec.fromArray(json.orientCoords),
|
|
195
|
+
orbitCoords: Vec.fromArray(json.orbitCoords)
|
|
196
|
+
})
|
|
197
|
+
}
|
|
169
198
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { IS_NODE, NUMBER_OF_CORES } from "../Utils/Constants.js";
|
|
2
|
+
import Color from "../Color/Color.js";
|
|
3
|
+
import { CHANNELS } from "../Tela/Tela.js";
|
|
4
|
+
|
|
5
|
+
//========================================================================================
|
|
6
|
+
/* *
|
|
7
|
+
* UTILS *
|
|
8
|
+
* */
|
|
9
|
+
//========================================================================================
|
|
10
|
+
|
|
11
|
+
const __Worker = IS_NODE ? (await import("node:worker_threads")).Worker : Worker;
|
|
12
|
+
class MyWorker {
|
|
13
|
+
constructor(path) {
|
|
14
|
+
this.path = path;
|
|
15
|
+
this.worker = new __Worker(path, { type: 'module' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
onMessage(lambda) {
|
|
19
|
+
if (IS_NODE) {
|
|
20
|
+
this.worker.removeAllListeners('message');
|
|
21
|
+
this.worker.on("message", lambda);
|
|
22
|
+
} else {
|
|
23
|
+
this.worker.onmessage = message => lambda(message.data);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
postMessage(message) {
|
|
28
|
+
return this.worker.postMessage(message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let WORKERS = [];
|
|
33
|
+
let prevSceneHash = undefined;
|
|
34
|
+
|
|
35
|
+
//========================================================================================
|
|
36
|
+
/* *
|
|
37
|
+
* MAIN *
|
|
38
|
+
* */
|
|
39
|
+
//========================================================================================
|
|
40
|
+
|
|
41
|
+
export function parallelWorkers(camera, scene, canvas, params = {}) {
|
|
42
|
+
// lazy loading workers
|
|
43
|
+
if (WORKERS.length === 0) {
|
|
44
|
+
WORKERS = [...Array(NUMBER_OF_CORES)]
|
|
45
|
+
.map(() => new MyWorker(`${IS_NODE ? "." : ""}/src/Camera/rayTraceWorker.js`));
|
|
46
|
+
}
|
|
47
|
+
const w = canvas.width;
|
|
48
|
+
const h = canvas.height;
|
|
49
|
+
const isNewScene = prevSceneHash !== scene.hash;
|
|
50
|
+
if (isNewScene) prevSceneHash = scene.hash;
|
|
51
|
+
return WORKERS.map((worker, k) => {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
worker.onMessage(message => {
|
|
54
|
+
const { image, startRow, endRow, } = message;
|
|
55
|
+
let index = 0;
|
|
56
|
+
const startIndex = CHANNELS * w * startRow;
|
|
57
|
+
const endIndex = CHANNELS * w * endRow;
|
|
58
|
+
for (let i = startIndex; i < endIndex; i += CHANNELS) {
|
|
59
|
+
canvas.setPxlData(i, Color.ofRGB(image[index++], image[index++], image[index++], image[index++]));
|
|
60
|
+
}
|
|
61
|
+
resolve();
|
|
62
|
+
})
|
|
63
|
+
const ratio = Math.floor(h / WORKERS.length);
|
|
64
|
+
|
|
65
|
+
const message = {
|
|
66
|
+
width: w,
|
|
67
|
+
height: h,
|
|
68
|
+
params: params,
|
|
69
|
+
startRow: k * ratio,
|
|
70
|
+
endRow: Math.min(h, (k + 1) * ratio),
|
|
71
|
+
camera: camera.serialize(),
|
|
72
|
+
scene: isNewScene ? scene.serialize() : undefined
|
|
73
|
+
};
|
|
74
|
+
worker.postMessage(message);
|
|
75
|
+
});
|
|
76
|
+
})
|
|
77
|
+
}
|
package/src/Camera/raster.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import Color from "../Color/Color.js";
|
|
2
2
|
import Line from "../Geometry/Line.js";
|
|
3
|
-
import
|
|
3
|
+
import Sphere from "../Geometry/Sphere.js";
|
|
4
4
|
import Mesh from "../Geometry/Mesh.js";
|
|
5
5
|
import Triangle from "../Geometry/Triangle.js";
|
|
6
6
|
import { Vec2 } from "../Vector/Vector.js";
|
|
7
7
|
import { getBiLinearTexColor, getDefaultTexColor, getTexColor } from "./common.js";
|
|
8
8
|
|
|
9
|
-
export function rasterGraphics(scene, camera, params) {
|
|
9
|
+
export function rasterGraphics(scene, camera, params = {}) {
|
|
10
10
|
const type2render = {
|
|
11
|
-
[
|
|
11
|
+
[Sphere.name]: rasterSphere,
|
|
12
12
|
[Line.name]: rasterLine,
|
|
13
13
|
[Triangle.name]: rasterTriangle,
|
|
14
14
|
[Mesh.name]: rasterMesh,
|
|
@@ -52,7 +52,7 @@ export function rasterGraphics(scene, camera, params) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
function
|
|
55
|
+
function rasterSphere({ canvas, camera, elem, w, h, zBuffer }) {
|
|
56
56
|
const point = elem;
|
|
57
57
|
const { distanceToPlane } = camera;
|
|
58
58
|
const { texCoord, texture, position, color, radius } = point;
|
|
@@ -71,6 +71,7 @@ function rasterPoint({ canvas, camera, elem, w, h, zBuffer }) {
|
|
|
71
71
|
y = Math.floor(y);
|
|
72
72
|
if (x < 0 || x >= w || y < 0 || y >= h) return;
|
|
73
73
|
const intRadius = Math.ceil((radius) * (distanceToPlane / z) * w);
|
|
74
|
+
const intRadiusSquare = intRadius * intRadius;
|
|
74
75
|
let finalColor = color;
|
|
75
76
|
if (
|
|
76
77
|
texture &&
|
|
@@ -83,6 +84,8 @@ function rasterPoint({ canvas, camera, elem, w, h, zBuffer }) {
|
|
|
83
84
|
for (let l = -intRadius; l < intRadius; l++) {
|
|
84
85
|
const xl = Math.max(0, Math.min(w - 1, x + k));
|
|
85
86
|
const yl = Math.floor(y + l);
|
|
87
|
+
const squareLength = k * k + l * l;
|
|
88
|
+
if (squareLength > intRadiusSquare) continue;
|
|
86
89
|
const [i, j] = canvas.canvas2grid(xl, yl);
|
|
87
90
|
const zBufferIndex = Math.floor(w * i + j);
|
|
88
91
|
if (z < zBuffer[zBufferIndex]) {
|
|
@@ -227,7 +230,7 @@ function rasterTriangle({ canvas, camera, elem, w, h, zBuffer, params }) {
|
|
|
227
230
|
params.bilinearTexture ?
|
|
228
231
|
getBiLinearTexColor(texUV, texture) :
|
|
229
232
|
getTexColor(texUV, texture) :
|
|
230
|
-
getDefaultTexColor(texUV);
|
|
233
|
+
c ? c : getDefaultTexColor(texUV); // TODO: review this
|
|
231
234
|
c = texColor;
|
|
232
235
|
}
|
|
233
236
|
const [i, j] = canvas.canvas2grid(x, y);
|
|
@@ -4,29 +4,30 @@ import Ray from "../Ray/Ray.js";
|
|
|
4
4
|
import Vec from "../Vector/Vector.js";
|
|
5
5
|
import { getBiLinearTexColor, getDefaultTexColor, getTexColor } from "./common.js";
|
|
6
6
|
|
|
7
|
-
export function rayTrace(scene, params
|
|
7
|
+
export function rayTrace(ray, scene, params) {
|
|
8
8
|
let { samplesPerPxl, bounces, variance, gamma, bilinearTexture } = params;
|
|
9
9
|
bounces = bounces ?? 10;
|
|
10
10
|
variance = variance ?? 0.001;
|
|
11
11
|
samplesPerPxl = samplesPerPxl ?? 1;
|
|
12
12
|
gamma = gamma ?? 0.5;
|
|
13
13
|
bilinearTexture = bilinearTexture ?? false;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const r = Ray(ray.init, ray.dir.add(epsilonOrto).normalize());
|
|
22
|
-
c = c.add(trace(r, scene, { bounces, bilinearTexture }));
|
|
23
|
-
}
|
|
24
|
-
return c.scale(invSamples).toGamma(gamma);
|
|
14
|
+
const invSamples = (bounces ?? 1) / samplesPerPxl
|
|
15
|
+
let c = Color.BLACK;
|
|
16
|
+
for (let i = 0; i < samplesPerPxl; i++) {
|
|
17
|
+
const epsilon = Vec.RANDOM(3).scale(variance);
|
|
18
|
+
const epsilonOrtho = epsilon.sub(ray.dir.scale(epsilon.dot(ray.dir)));
|
|
19
|
+
const r = Ray(ray.init, ray.dir.add(epsilonOrtho).normalize());
|
|
20
|
+
c = c.add(trace(r, scene, { bounces, bilinearTexture }));
|
|
25
21
|
}
|
|
22
|
+
return c.scale(invSamples).toGamma(gamma);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getRayTracer(scene, params = {}) {
|
|
26
|
+
const lambda = ray => rayTrace(ray, scene, params);
|
|
26
27
|
return lambda;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
function trace(ray, scene, options) {
|
|
30
|
+
export function trace(ray, scene, options) {
|
|
30
31
|
const { bounces, bilinearTexture } = options;
|
|
31
32
|
if (bounces < 0) return Color.BLACK;
|
|
32
33
|
const hit = scene.interceptWithRay(ray);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Camera from "./Camera.js";
|
|
2
|
+
import { rayTrace } from "./rayTrace.js";
|
|
3
|
+
import { CHANNELS } from "../Tela/Tela.js";
|
|
4
|
+
import { IS_NODE } from "../Utils/Constants.js";
|
|
5
|
+
import { deserializeScene } from "../Scene/utils.js";
|
|
6
|
+
import Color from "../Color/Color.js";
|
|
7
|
+
|
|
8
|
+
const parentPort = IS_NODE ? (await import("node:worker_threads")).parentPort : undefined;
|
|
9
|
+
|
|
10
|
+
let scene = undefined;
|
|
11
|
+
|
|
12
|
+
function getScene(serializedScene) {
|
|
13
|
+
return deserializeScene(serializedScene).then(s => s.rebuild());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function main(inputs) {
|
|
17
|
+
const {
|
|
18
|
+
startRow,
|
|
19
|
+
endRow,
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
params,
|
|
23
|
+
scene: serializedScene,
|
|
24
|
+
camera: serializedCamera,
|
|
25
|
+
} = inputs;
|
|
26
|
+
scene = serializedScene ? await getScene(serializedScene) : scene;
|
|
27
|
+
const camera = Camera.deserialize(serializedCamera);
|
|
28
|
+
const rayGen = camera.rayFromImage(width, height);
|
|
29
|
+
const bufferSize = width * (endRow - startRow + 1) * CHANNELS;
|
|
30
|
+
const image = new Float32Array(bufferSize);
|
|
31
|
+
let index = 0;
|
|
32
|
+
// the order does matter
|
|
33
|
+
for (let y = startRow; y < endRow; y++) {
|
|
34
|
+
for (let x = 0; x < width; x++) {
|
|
35
|
+
const ray = rayGen(x, height - 1 - y);
|
|
36
|
+
const color = scene ? rayTrace(ray, scene, params) : Color.random();
|
|
37
|
+
image[index++] = color.red;
|
|
38
|
+
image[index++] = color.green;
|
|
39
|
+
image[index++] = color.blue;
|
|
40
|
+
image[index++] = color.alpha;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { image, startRow, endRow };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if (IS_NODE) {
|
|
48
|
+
parentPort.on("message", async message => {
|
|
49
|
+
const input = message;
|
|
50
|
+
const output = await main(input);
|
|
51
|
+
parentPort.postMessage(output);
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
onmessage = async message => {
|
|
55
|
+
const input = message.data;
|
|
56
|
+
const output = await main(input);
|
|
57
|
+
postMessage(output);
|
|
58
|
+
};
|
|
59
|
+
}
|
package/src/Color/Color.js
CHANGED
package/src/Geometry/Box.js
CHANGED
package/src/Geometry/Geometry.js
CHANGED
|
@@ -35,4 +35,20 @@ export default class Geometry {
|
|
|
35
35
|
interceptWithRay(ray) {
|
|
36
36
|
throw Error("Not implemented");
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
sample() {
|
|
40
|
+
throw Error("Not implemented");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
equals(obj) {
|
|
44
|
+
return this === obj;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
serialize() {
|
|
48
|
+
throw Error("Not implemented");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static deserialize(json) {
|
|
52
|
+
throw Error("Not implemented");
|
|
53
|
+
}
|
|
38
54
|
}
|
package/src/Geometry/Line.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Box from "./Box.js";
|
|
2
2
|
import Color from "../Color/Color.js";
|
|
3
|
-
import { Diffuse } from "../Material/Material.js";
|
|
3
|
+
import { Diffuse, MATERIALS } from "../Material/Material.js";
|
|
4
4
|
import { clamp } from "../Utils/Math.js";
|
|
5
|
-
import { Vec2, Vec3 } from "../Vector/Vector.js";
|
|
5
|
+
import Vec, { Vec2, Vec3 } from "../Vector/Vector.js";
|
|
6
6
|
|
|
7
7
|
export default class Line {
|
|
8
8
|
constructor({ name, positions, colors, texCoords, normals, texture, radius, emissive, material }) {
|
|
@@ -76,6 +76,23 @@ export default class Line {
|
|
|
76
76
|
return this.distanceToPoint(p) < 0;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
serialize() {
|
|
80
|
+
//TODO's
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static deserialize(json) {
|
|
84
|
+
const { type, args } = json.material;
|
|
85
|
+
return Line
|
|
86
|
+
.builder()
|
|
87
|
+
.name(json.name)
|
|
88
|
+
.radius(json.radius)
|
|
89
|
+
.positions(...json.positions.map(x => Vec.fromArray(x)))
|
|
90
|
+
.colors(...json.colors.map(x => new Color(x)))
|
|
91
|
+
.emissive(json.emissive)
|
|
92
|
+
.material(MATERIALS[type](...args))
|
|
93
|
+
.build()
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
static builder() {
|
|
80
97
|
return new LineBuilder();
|
|
81
98
|
}
|
package/src/Geometry/Mesh.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Vec3, Vec2 } from "../Vector/Vector.js";
|
|
2
|
-
import
|
|
2
|
+
import Sphere from "./Sphere.js"
|
|
3
3
|
import Line from "./Line.js";
|
|
4
4
|
import Triangle from "./Triangle.js";
|
|
5
5
|
import { groupBy } from "../Utils/Utils.js";
|
|
6
6
|
import { Diffuse } from "../Material/Material.js";
|
|
7
7
|
import KScene from "../Scene/KScene.js"
|
|
8
|
+
import { UNIT_BOX_LINES, UNIT_BOX_VERTEX } from "../Utils/Utils3D.js";
|
|
8
9
|
//========================================================================================
|
|
9
10
|
/* *
|
|
10
11
|
* CONSTANTS *
|
|
@@ -32,7 +33,7 @@ export default class Mesh {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
_init() {
|
|
35
|
-
if(this._meshScene) return this;
|
|
36
|
+
if (this._meshScene) return this;
|
|
36
37
|
this._meshScene = new KScene();
|
|
37
38
|
this._meshScene.addList(this.asTriangles());
|
|
38
39
|
this._meshScene.rebuild();
|
|
@@ -144,7 +145,7 @@ export default class Mesh {
|
|
|
144
145
|
for (let j = 0; j < 3; j++) {
|
|
145
146
|
const pointName = `${this.name}_${verticesIndexes[j]}`
|
|
146
147
|
if (!(pointName in points)) {
|
|
147
|
-
points[pointName] =
|
|
148
|
+
points[pointName] = Sphere
|
|
148
149
|
.builder()
|
|
149
150
|
.name(pointName)
|
|
150
151
|
.radius(radius)
|
|
@@ -215,6 +216,14 @@ export default class Mesh {
|
|
|
215
216
|
return triangles;
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
serialize() {
|
|
220
|
+
// TODO
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
deserialize(jsonMesh) {
|
|
224
|
+
// TODO
|
|
225
|
+
}
|
|
226
|
+
|
|
218
227
|
static readObj(objFile, name = `Mesh_${MESH_COUNTER++}`) {
|
|
219
228
|
const vertices = [];
|
|
220
229
|
const normals = [];
|
|
@@ -263,7 +272,7 @@ export default class Mesh {
|
|
|
263
272
|
|
|
264
273
|
static ofBox(box, name) {
|
|
265
274
|
const vertices = UNIT_BOX_VERTEX.map(v => v.mul(box.diagonal).add(box.min))
|
|
266
|
-
return new Mesh({ name: name, vertices, faces:
|
|
275
|
+
return new Mesh({ name: name, vertices, faces: UNIT_BOX_LINES.map(indx => ({ vertices: indx })) });
|
|
267
276
|
}
|
|
268
277
|
}
|
|
269
278
|
|
package/src/Geometry/Path.js
CHANGED
|
@@ -15,14 +15,17 @@ export default class Path {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
distanceToPoint() {
|
|
18
|
+
// TODO
|
|
18
19
|
throw Error("No implementation");
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
normalToPoint() {
|
|
23
|
+
// TODO
|
|
22
24
|
throw Error("No implementation");
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
interceptWithRay(ray) {
|
|
28
|
+
// TODO
|
|
26
29
|
throw Error("No implementation");
|
|
27
30
|
}
|
|
28
31
|
|