@tinypoly/gpu-renderer 0.2.4 → 0.3.0
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/ARCHITECTURE.md +48 -10
- package/README.md +2 -0
- package/dist/accumulator.d.ts.map +1 -1
- package/dist/accumulator.js +1 -0
- package/dist/accumulator.js.map +1 -1
- package/dist/denoise.js +2 -2
- package/dist/denoise.js.map +1 -1
- package/dist/device.d.ts.map +1 -1
- package/dist/device.js +9 -0
- package/dist/device.js.map +1 -1
- package/dist/frameLayout.d.ts.map +1 -1
- package/dist/frameLayout.js +5 -0
- package/dist/frameLayout.js.map +1 -1
- package/dist/geometry.d.ts +48 -8
- package/dist/geometry.d.ts.map +1 -1
- package/dist/geometry.js +409 -134
- package/dist/geometry.js.map +1 -1
- package/dist/grass.d.ts.map +1 -1
- package/dist/grass.js +2 -1
- package/dist/grass.js.map +1 -1
- package/dist/imageResolve.js +2 -2
- package/dist/imageResolve.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/instancing.d.ts +100 -0
- package/dist/instancing.d.ts.map +1 -0
- package/dist/instancing.js +286 -0
- package/dist/instancing.js.map +1 -0
- package/dist/lensPass.d.ts +2 -0
- package/dist/lensPass.d.ts.map +1 -1
- package/dist/lensPass.js +7 -4
- package/dist/lensPass.js.map +1 -1
- package/dist/lights.d.ts.map +1 -1
- package/dist/lights.js +8 -5
- package/dist/lights.js.map +1 -1
- package/dist/memoryProbe.d.ts +76 -0
- package/dist/memoryProbe.d.ts.map +1 -0
- package/dist/memoryProbe.js +197 -0
- package/dist/memoryProbe.js.map +1 -0
- package/dist/oidnPass.d.ts +6 -0
- package/dist/oidnPass.d.ts.map +1 -1
- package/dist/oidnPass.js +18 -1
- package/dist/oidnPass.js.map +1 -1
- package/dist/pathTracePass.d.ts +15 -2
- package/dist/pathTracePass.d.ts.map +1 -1
- package/dist/pathTracePass.js +38 -16
- package/dist/pathTracePass.js.map +1 -1
- package/dist/renderer.d.ts +13 -0
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +248 -41
- package/dist/renderer.js.map +1 -1
- package/dist/sceneUpload.d.ts +26 -8
- package/dist/sceneUpload.d.ts.map +1 -1
- package/dist/sceneUpload.js +97 -27
- package/dist/sceneUpload.js.map +1 -1
- package/dist/shaderModule.d.ts.map +1 -1
- package/dist/shaderModule.js +2 -1
- package/dist/shaderModule.js.map +1 -1
- package/dist/skyBake.d.ts.map +1 -1
- package/dist/skyBake.js +5 -3
- package/dist/skyBake.js.map +1 -1
- package/dist/texturePack.d.ts +43 -3
- package/dist/texturePack.d.ts.map +1 -1
- package/dist/texturePack.js +209 -37
- package/dist/texturePack.js.map +1 -1
- package/dist/wgsl/bvh.wgsl.d.ts +5 -1
- package/dist/wgsl/bvh.wgsl.d.ts.map +1 -1
- package/dist/wgsl/bvh.wgsl.js +105 -15
- package/dist/wgsl/bvh.wgsl.js.map +1 -1
- package/dist/wgsl/common.wgsl.d.ts.map +1 -1
- package/dist/wgsl/common.wgsl.js +92 -11
- package/dist/wgsl/common.wgsl.js.map +1 -1
- package/dist/wgsl/lens.wgsl.d.ts +1 -1
- package/dist/wgsl/lens.wgsl.d.ts.map +1 -1
- package/dist/wgsl/lens.wgsl.js +14 -2
- package/dist/wgsl/lens.wgsl.js.map +1 -1
- package/dist/wgsl/surface.wgsl.d.ts +1 -1
- package/dist/wgsl/surface.wgsl.d.ts.map +1 -1
- package/dist/wgsl/surface.wgsl.js +24 -14
- package/dist/wgsl/surface.wgsl.js.map +1 -1
- package/dist/wgsl/texture.wgsl.d.ts +1 -1
- package/dist/wgsl/texture.wgsl.d.ts.map +1 -1
- package/dist/wgsl/texture.wgsl.js +11 -0
- package/dist/wgsl/texture.wgsl.js.map +1 -1
- package/package.json +1 -1
- package/src/accumulator.ts +1 -0
- package/src/denoise.ts +2 -2
- package/src/device.ts +12 -0
- package/src/frameLayout.ts +5 -0
- package/src/geometry.ts +527 -133
- package/src/grass.ts +2 -1
- package/src/imageResolve.ts +2 -2
- package/src/index.ts +1 -0
- package/src/instancing.ts +415 -0
- package/src/lensPass.ts +9 -4
- package/src/lights.ts +9 -5
- package/src/memoryProbe.ts +275 -0
- package/src/oidnPass.ts +21 -1
- package/src/pathTracePass.ts +42 -18
- package/src/renderer.ts +308 -47
- package/src/sceneUpload.ts +139 -36
- package/src/shaderModule.ts +2 -1
- package/src/skyBake.ts +5 -3
- package/src/texturePack.ts +274 -43
- package/src/wgsl/bvh.wgsl.ts +105 -15
- package/src/wgsl/common.wgsl.ts +92 -11
- package/src/wgsl/lens.wgsl.ts +14 -2
- package/src/wgsl/surface.wgsl.ts +24 -14
- package/src/wgsl/texture.wgsl.ts +11 -0
package/src/wgsl/common.wgsl.ts
CHANGED
|
@@ -137,13 +137,16 @@ const TEXTURE_TABLE: u32 = ${TEXTURE_TABLE}u;
|
|
|
137
137
|
// A WebGPU device only has to offer eight storage buffers per stage, so arrays of the same kind share one binding and
|
|
138
138
|
// the uniform says where the second one starts. See sceneUpload.ts, which joins them.
|
|
139
139
|
@group(0) @binding(0) var<uniform> frame: Frame;
|
|
140
|
-
// Nine coordinates per triangle, in the hierarchy's leaf order: see leafOf in bvh.wgsl.ts.
|
|
140
|
+
// Nine coordinates per stored triangle, in the hierarchy's leaf order: see leafOf in bvh.wgsl.ts. A prototype's are
|
|
141
|
+
// in object space.
|
|
141
142
|
@group(0) @binding(1) var<storage, read> positions: array<f32>;
|
|
142
|
-
//
|
|
143
|
-
// frame.varyingOffset.
|
|
143
|
+
// Per vertex: the UV sets from frame.uvOffset and vertex colours from frame.colorOffset; then shader varyings, per
|
|
144
|
+
// corner, from frame.varyingOffset.
|
|
144
145
|
@group(0) @binding(2) var<storage, read> attributes: array<f32>;
|
|
145
|
-
// One material index per triangle, then the hierarchy's triangle order from frame.orderOffset on (the
|
|
146
|
-
// position of the leaf order), then from frame.leafOffset on the other way (the position of
|
|
146
|
+
// One material index per triangle a hit can name, then the hierarchy's triangle order from frame.orderOffset on (the
|
|
147
|
+
// stored triangle at each position of the leaf order), then from frame.leafOffset on the other way (the position of
|
|
148
|
+
// each stored triangle), the instance of every instanced triangle from frame.instanceOffset, the three vertices of each
|
|
149
|
+
// stored triangle from frame.cornerOffset and every vertex's folded normal from frame.normalOffset.
|
|
147
150
|
@group(0) @binding(3) var<storage, read> indices: array<u32>;
|
|
148
151
|
@group(0) @binding(4) var<storage, read> materials: array<Material>;
|
|
149
152
|
@group(0) @binding(5) var<storage, read> nodes: array<BvhNode>;
|
|
@@ -163,24 +166,102 @@ fn orderedTriangle(at: u32) -> u32 {
|
|
|
163
166
|
return indices[frame.orderOffset + at];
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
/** What a triangle of the scene's own belongs to: no instance. */
|
|
170
|
+
const NO_INSTANCE: u32 = 0xffffffffu;
|
|
171
|
+
|
|
172
|
+
/** The instance a triangle a hit named belongs to, or NO_INSTANCE for one of the scene's own. */
|
|
173
|
+
fn instanceOf(triangle: u32) -> u32 {
|
|
174
|
+
if (!INSTANCES || triangle < frame.sceneTriangles) {
|
|
175
|
+
return NO_INSTANCE;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return indices[frame.instanceOffset + triangle - frame.sceneTriangles];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** One word of an instance's record: see INSTANCE_WORDS in instancing.ts. */
|
|
182
|
+
fn instanceWord(instance: u32, word: u32) -> u32 {
|
|
183
|
+
return textures[frame.instanceData + instance * 28u + word];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** A row of an instance's transforms: 0 to 2 take object space to world, 3 to 5 back. */
|
|
187
|
+
fn instanceRow(instance: u32, row: u32) -> vec4<f32> {
|
|
188
|
+
let at = frame.instanceData + instance * 28u + row * 4u;
|
|
189
|
+
|
|
190
|
+
return vec4<f32>(bitcast<f32>(textures[at]), bitcast<f32>(textures[at + 1u]), bitcast<f32>(textures[at + 2u]),
|
|
191
|
+
bitcast<f32>(textures[at + 3u]));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Where a named triangle is stored: itself, or for an instance's, the prototype's triangle it repeats. */
|
|
195
|
+
fn storedTriangle(triangle: u32, instance: u32) -> u32 {
|
|
196
|
+
if (instance == NO_INSTANCE) {
|
|
197
|
+
return triangle;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return instanceWord(instance, 25u) + triangle - instanceWord(instance, 26u);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** A point of an instance's prototype, in world space. */
|
|
204
|
+
fn instancePoint(instance: u32, p: vec3<f32>) -> vec3<f32> {
|
|
205
|
+
let q = vec4<f32>(p, 1.0);
|
|
206
|
+
|
|
207
|
+
return vec3<f32>(dot(instanceRow(instance, 0u), q), dot(instanceRow(instance, 1u), q), dot(instanceRow(instance, 2u), q));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** A world space point (w 1) or direction (w 0), in an instance's object space. */
|
|
211
|
+
fn instanceToObject(instance: u32, v: vec3<f32>, w: f32) -> vec3<f32> {
|
|
212
|
+
let q = vec4<f32>(v, w);
|
|
213
|
+
|
|
214
|
+
return vec3<f32>(dot(instanceRow(instance, 3u), q), dot(instanceRow(instance, 4u), q), dot(instanceRow(instance, 5u), q));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** An object space normal in world space, by the inverse transpose; not normalized. */
|
|
218
|
+
fn instanceNormal(instance: u32, n: vec3<f32>) -> vec3<f32> {
|
|
219
|
+
return n.x * instanceRow(instance, 3u).xyz + n.y * instanceRow(instance, 4u).xyz + n.z * instanceRow(instance, 5u).xyz;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** The three vertices of a triangle a hit named. */
|
|
223
|
+
fn triangleCorners(triangle: u32) -> vec3<u32> {
|
|
224
|
+
let at = frame.cornerOffset + storedTriangle(triangle, instanceOf(triangle)) * 3u;
|
|
225
|
+
|
|
226
|
+
return vec3<u32>(indices[at], indices[at + 1u], indices[at + 2u]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** One UV set of a vertex: four components, two per set. */
|
|
230
|
+
fn vertexUv(vertex: u32, channel: u32) -> vec2<f32> {
|
|
231
|
+
let at = frame.uvOffset + vertex * 4u + channel * 2u;
|
|
169
232
|
|
|
170
233
|
return vec2<f32>(attributes[at], attributes[at + 1u]);
|
|
171
234
|
}
|
|
172
235
|
|
|
173
|
-
/** A
|
|
174
|
-
fn
|
|
236
|
+
/** A vertex's colour, or white when the scene has none. */
|
|
237
|
+
fn vertexColor(vertex: u32) -> vec4<f32> {
|
|
175
238
|
if (frame.hasColors == 0u) {
|
|
176
239
|
return vec4<f32>(1.0);
|
|
177
240
|
}
|
|
178
241
|
|
|
179
|
-
let at = frame.colorOffset +
|
|
242
|
+
let at = frame.colorOffset + vertex * 4u;
|
|
180
243
|
|
|
181
244
|
return vec4<f32>(attributes[at], attributes[at + 1u], attributes[at + 2u], attributes[at + 3u]);
|
|
182
245
|
}
|
|
183
246
|
|
|
247
|
+
/** A normal folded onto an octahedron in two signed 16 bit halves, as sceneUpload.ts folds it; zero for NO_NORMAL. */
|
|
248
|
+
fn vertexNormal(vertex: u32) -> vec3<f32> {
|
|
249
|
+
let code = indices[frame.normalOffset + vertex];
|
|
250
|
+
|
|
251
|
+
if (code == 0x80008000u) {
|
|
252
|
+
return vec3<f32>(0.0);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let e = unpack2x16snorm(code);
|
|
256
|
+
var n = vec3<f32>(e.x, e.y, 1.0 - abs(e.x) - abs(e.y));
|
|
257
|
+
let t = max(-n.z, 0.0);
|
|
258
|
+
|
|
259
|
+
n.x = n.x + select(t, -t, n.x >= 0.0);
|
|
260
|
+
n.y = n.y + select(t, -t, n.y >= 0.0);
|
|
261
|
+
|
|
262
|
+
return normalize(n);
|
|
263
|
+
}
|
|
264
|
+
|
|
184
265
|
fn extraFloat(at: u32) -> f32 {
|
|
185
266
|
return bitcast<f32>(textures[at]);
|
|
186
267
|
}
|
package/src/wgsl/lens.wgsl.ts
CHANGED
|
@@ -50,6 +50,8 @@ struct Lens {
|
|
|
50
50
|
autoExposure: f32,
|
|
51
51
|
// The share of a dark scene's darkness the meter keeps.
|
|
52
52
|
darkAdaptation: f32,
|
|
53
|
+
// The exposure applied after the lens, the meter's aside, which the rays are limited against.
|
|
54
|
+
exposure: f32,
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
const PI: f32 = 3.14159265358979;
|
|
@@ -57,6 +59,12 @@ const PI: f32 = 3.14159265358979;
|
|
|
57
59
|
/** Taps along each of the aperture's rays. */
|
|
58
60
|
const STREAK_TAPS: i32 = 24;
|
|
59
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The brightest a tap of the rays counts as, in multiples of the exposed white. A lamp is thousands of times white,
|
|
64
|
+
* which would carry its rays across the frame at full length; past this they stay as short as a camera shows them.
|
|
65
|
+
*/
|
|
66
|
+
const GLARE_CEILING: f32 = 4.0;
|
|
67
|
+
|
|
60
68
|
@group(0) @binding(0) var<uniform> lens: Lens;
|
|
61
69
|
@group(0) @binding(1) var<storage, read> accumulation: array<vec4<f32>>;
|
|
62
70
|
@group(0) @binding(2) var<storage, read> denoised: array<vec4<f32>>;
|
|
@@ -342,6 +350,8 @@ fn streaks(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
342
350
|
}
|
|
343
351
|
|
|
344
352
|
let centre = vec2<f32>(id.xy) + 0.5;
|
|
353
|
+
// The exposure the image will have: the setting's, and the meter's when it meters.
|
|
354
|
+
let exposure = lens.exposure * select(1.0, metered[0].x, lens.autoExposure > 0.0);
|
|
345
355
|
var sum = vec3<f32>(0.0);
|
|
346
356
|
var total = 0.0;
|
|
347
357
|
|
|
@@ -351,9 +361,11 @@ fn streaks(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
351
361
|
|
|
352
362
|
for (var j = 1; j <= STREAK_TAPS; j = j + 1) {
|
|
353
363
|
let t = f32(j) / f32(STREAK_TAPS);
|
|
354
|
-
let weight = exp(-
|
|
364
|
+
let weight = exp(-6.0 * t);
|
|
365
|
+
|
|
366
|
+
let light = downBilinear(lens.sourceOffset, lens.sourceSize, centre - along * (t * lens.glareLength));
|
|
355
367
|
|
|
356
|
-
sum = sum +
|
|
368
|
+
sum = sum + light * (weight / max(1.0, luminance(light) * exposure / GLARE_CEILING));
|
|
357
369
|
total = total + weight;
|
|
358
370
|
}
|
|
359
371
|
}
|
package/src/wgsl/surface.wgsl.ts
CHANGED
|
@@ -82,15 +82,19 @@ fn trianglePointAt(triangle: u32, bary: vec2<f32>) -> vec3<f32> {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
fn triangleUv(triangle: u32, bary: vec2<f32>, channel: u32) -> vec2<f32> {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
let corners = triangleCorners(triangle);
|
|
86
|
+
|
|
87
|
+
return vertexUv(corners.x, channel) * (1.0 - bary.x - bary.y)
|
|
88
|
+
+ vertexUv(corners.y, channel) * bary.x
|
|
89
|
+
+ vertexUv(corners.z, channel) * bary.y;
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
fn triangleColor(triangle: u32, bary: vec2<f32>) -> vec4<f32> {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
let corners = triangleCorners(triangle);
|
|
94
|
+
|
|
95
|
+
return vertexColor(corners.x) * (1.0 - bary.x - bary.y)
|
|
96
|
+
+ vertexColor(corners.y) * bary.x
|
|
97
|
+
+ vertexColor(corners.z) * bary.y;
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
/** A map slot, sampled through the UV set the texture asks for. Missing maps read as one, so a multiply is always safe. */
|
|
@@ -362,9 +366,10 @@ fn tangentFrame(triangle: u32, channel: u32, normal: vec3<f32>) -> TangentFrame
|
|
|
362
366
|
let p0 = triangleVertex(triangle, 0u);
|
|
363
367
|
let edge1 = triangleVertex(triangle, 1u) - p0;
|
|
364
368
|
let edge2 = triangleVertex(triangle, 2u) - p0;
|
|
365
|
-
let
|
|
366
|
-
let
|
|
367
|
-
let
|
|
369
|
+
let corners = triangleCorners(triangle);
|
|
370
|
+
let uv0 = vertexUv(corners.x, channel);
|
|
371
|
+
let delta1 = vertexUv(corners.y, channel) - uv0;
|
|
372
|
+
let delta2 = vertexUv(corners.z, channel) - uv0;
|
|
368
373
|
let determinant = delta1.x * delta2.y - delta2.x * delta1.y;
|
|
369
374
|
|
|
370
375
|
// A triangle with no UV area has no tangent frame to offer.
|
|
@@ -460,12 +465,17 @@ fn mappedNormal(m: u32, triangle: u32, bary: vec2<f32>, normal: vec3<f32>) -> ve
|
|
|
460
465
|
|
|
461
466
|
/** The interpolated vertex normal as the mesh authored it, pointing out of a closed volume. */
|
|
462
467
|
fn authoredNormal(triangle: u32, bary: vec2<f32>) -> vec3<f32> {
|
|
463
|
-
let
|
|
464
|
-
let
|
|
465
|
-
let
|
|
466
|
-
|
|
468
|
+
let instance = instanceOf(triangle);
|
|
469
|
+
let at = frame.cornerOffset + storedTriangle(triangle, instance) * 3u;
|
|
470
|
+
let n = vertexNormal(indices[at]) * (1.0 - bary.x - bary.y)
|
|
471
|
+
+ vertexNormal(indices[at + 1u]) * bary.x
|
|
472
|
+
+ vertexNormal(indices[at + 2u]) * bary.y;
|
|
473
|
+
|
|
474
|
+
if (instance != NO_INSTANCE) {
|
|
475
|
+
return normalize(instanceNormal(instance, n));
|
|
476
|
+
}
|
|
467
477
|
|
|
468
|
-
return normalize(
|
|
478
|
+
return normalize(n);
|
|
469
479
|
}
|
|
470
480
|
|
|
471
481
|
/** The interpolated vertex normal, or the face's for flat shading, turned to face the ray. */
|
package/src/wgsl/texture.wgsl.ts
CHANGED
|
@@ -12,6 +12,7 @@ const TEXTURE_FLOAT: u32 = 1u;
|
|
|
12
12
|
const TEXTURE_SRGB: u32 = 2u;
|
|
13
13
|
const TEXTURE_NEAREST: u32 = 4u;
|
|
14
14
|
const TEXTURE_FLIP_Y: u32 = 8u;
|
|
15
|
+
const TEXTURE_SINGLE: u32 = 32u;
|
|
15
16
|
|
|
16
17
|
// Three's own wrap constants. Anything else clamps, as it does in the CPU renderer.
|
|
17
18
|
const WRAP_REPEAT: u32 = 1000u;
|
|
@@ -47,6 +48,16 @@ fn readTexel(info: TextureInfo, x: i32, y: i32) -> vec4<f32> {
|
|
|
47
48
|
bitcast<f32>(textures[at + 3u]));
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
// The only channel the scene reads, four texels to a word, handed back in every channel: alpha as it is, the colour
|
|
52
|
+
// decoded where the texture is sRGB, as a full texel's would be.
|
|
53
|
+
if ((info.flags & TEXTURE_SINGLE) != 0u) {
|
|
54
|
+
let code = (textures[info.offset + (index >> 2u)] >> ((index & 3u) * 8u)) & 0xffu;
|
|
55
|
+
let raw = f32(code) / 255.0;
|
|
56
|
+
let colour = select(raw, bitcast<f32>(textures[SRGB_TABLE + code]), (info.flags & TEXTURE_SRGB) != 0u);
|
|
57
|
+
|
|
58
|
+
return vec4<f32>(colour, colour, colour, raw);
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
let word = textures[info.offset + index];
|
|
51
62
|
|
|
52
63
|
// The decode is a table of the 256 codes rather than a power per channel and per texel: the same values, read.
|