@plasius/gpu-renderer 0.1.15 → 0.2.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/CHANGELOG.md +5 -3
- package/README.md +8 -0
- package/dist/index.cjs +251 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +248 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +78 -0
- package/src/index.js +3 -0
- package/src/wavefront-compute.js +280 -0
package/dist/index.js
CHANGED
|
@@ -167,6 +167,25 @@ function normalize(value, fallback = [0, 0, 1]) {
|
|
|
167
167
|
}
|
|
168
168
|
return [value[0] / length, value[1] / length, value[2] / length];
|
|
169
169
|
}
|
|
170
|
+
function hashUint32(value) {
|
|
171
|
+
let x = value >>> 0;
|
|
172
|
+
x = ((x >>> 16 ^ x) >>> 0) * 73244475 >>> 0;
|
|
173
|
+
x = ((x >>> 16 ^ x) >>> 0) * 73244475 >>> 0;
|
|
174
|
+
return (x >>> 16 ^ x) >>> 0;
|
|
175
|
+
}
|
|
176
|
+
function mixSeed(pixelId, sampleId, bounce, frameIndex, dimension) {
|
|
177
|
+
let x = (pixelId >>> 0) * 747796405 ^ (sampleId >>> 0) * 2891336453 ^ (bounce >>> 0) * 277803737 ^ (frameIndex >>> 0) * 1442695041 ^ (dimension >>> 0) * 1597334677;
|
|
178
|
+
x >>>= 0;
|
|
179
|
+
x ^= x >>> 16;
|
|
180
|
+
x = x * 2146121005 >>> 0;
|
|
181
|
+
x ^= x >>> 15;
|
|
182
|
+
x = x * 2221713035 >>> 0;
|
|
183
|
+
x ^= x >>> 16;
|
|
184
|
+
return x >>> 0;
|
|
185
|
+
}
|
|
186
|
+
function random01FromSeed(seed) {
|
|
187
|
+
return (hashUint32(seed) & 16777215) / 16777215;
|
|
188
|
+
}
|
|
170
189
|
function getArrayLikeLength(value) {
|
|
171
190
|
return Array.isArray(value) || ArrayBuffer.isView(value) ? value.length : 0;
|
|
172
191
|
}
|
|
@@ -765,6 +784,29 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
765
784
|
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure))
|
|
766
785
|
});
|
|
767
786
|
}
|
|
787
|
+
function evaluateReferenceEnvironmentRadiance(config, origin, direction) {
|
|
788
|
+
void origin;
|
|
789
|
+
const rayDirection = normalize(direction, [0, 1, 0]);
|
|
790
|
+
const upFactor = clamp(rayDirection[1] * 0.5 + 0.5, 0, 1);
|
|
791
|
+
const sunDirection = normalize(
|
|
792
|
+
config.environmentLighting?.sunDirection ?? DEFAULT_ENVIRONMENT_LIGHTING.sunDirection,
|
|
793
|
+
DEFAULT_ENVIRONMENT_LIGHTING.sunDirection
|
|
794
|
+
);
|
|
795
|
+
const sunGlow = Math.pow(clamp(dot(rayDirection, sunDirection), 0, 1), 192);
|
|
796
|
+
const horizonColor = config.environmentLighting?.horizonColor ?? DEFAULT_ENVIRONMENT_LIGHTING.horizonColor;
|
|
797
|
+
const zenithColor = config.environmentLighting?.zenithColor ?? DEFAULT_ENVIRONMENT_LIGHTING.zenithColor;
|
|
798
|
+
const sunColor = config.environmentLighting?.sunColor ?? DEFAULT_ENVIRONMENT_LIGHTING.sunColor;
|
|
799
|
+
const intensity = Math.max(
|
|
800
|
+
1e-4,
|
|
801
|
+
Number(config.environmentLighting?.intensity ?? DEFAULT_ENVIRONMENT_LIGHTING.intensity)
|
|
802
|
+
);
|
|
803
|
+
return Object.freeze([
|
|
804
|
+
(horizonColor[0] * (1 - upFactor) + zenithColor[0] * upFactor + sunColor[0] * sunGlow) * intensity,
|
|
805
|
+
(horizonColor[1] * (1 - upFactor) + zenithColor[1] * upFactor + sunColor[1] * sunGlow) * intensity,
|
|
806
|
+
(horizonColor[2] * (1 - upFactor) + zenithColor[2] * upFactor + sunColor[2] * sunGlow) * intensity,
|
|
807
|
+
1
|
|
808
|
+
]);
|
|
809
|
+
}
|
|
768
810
|
function resolveEnvironmentPortalMode(value, hasPortals) {
|
|
769
811
|
if (value === void 0 || value === null) {
|
|
770
812
|
return hasPortals ? 2 : 0;
|
|
@@ -1293,6 +1335,209 @@ function createTiles(width, height, tileSize) {
|
|
|
1293
1335
|
}
|
|
1294
1336
|
return Object.freeze(tiles);
|
|
1295
1337
|
}
|
|
1338
|
+
function normalizeReferenceTile(config, tileInput = {}) {
|
|
1339
|
+
const tileX = clamp(
|
|
1340
|
+
readNonNegativeInteger("tile.x", tileInput.x, 0),
|
|
1341
|
+
0,
|
|
1342
|
+
Math.max(0, config.width - 1)
|
|
1343
|
+
);
|
|
1344
|
+
const tileY = clamp(
|
|
1345
|
+
readNonNegativeInteger("tile.y", tileInput.y, 0),
|
|
1346
|
+
0,
|
|
1347
|
+
Math.max(0, config.height - 1)
|
|
1348
|
+
);
|
|
1349
|
+
const tileWidth = clamp(
|
|
1350
|
+
readPositiveInteger("tile.width", tileInput.width, config.width - tileX),
|
|
1351
|
+
1,
|
|
1352
|
+
config.width - tileX
|
|
1353
|
+
);
|
|
1354
|
+
const tileHeight = clamp(
|
|
1355
|
+
readPositiveInteger("tile.height", tileInput.height, config.height - tileY),
|
|
1356
|
+
1,
|
|
1357
|
+
config.height - tileY
|
|
1358
|
+
);
|
|
1359
|
+
return Object.freeze({
|
|
1360
|
+
x: tileX,
|
|
1361
|
+
y: tileY,
|
|
1362
|
+
width: tileWidth,
|
|
1363
|
+
height: tileHeight
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
function repairReferenceShadingNormal(geometricNormal, shadingNormal) {
|
|
1367
|
+
const normal = normalize(shadingNormal, geometricNormal);
|
|
1368
|
+
return dot(normal, geometricNormal) < 0 ? scale(normal, -1) : normal;
|
|
1369
|
+
}
|
|
1370
|
+
function readOptionalMaxDistance(value) {
|
|
1371
|
+
if (value === void 0 || value === null) {
|
|
1372
|
+
return Number.POSITIVE_INFINITY;
|
|
1373
|
+
}
|
|
1374
|
+
const numeric = Number(value);
|
|
1375
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
1376
|
+
throw new Error("maxDistance must be a positive finite number when provided.");
|
|
1377
|
+
}
|
|
1378
|
+
return numeric;
|
|
1379
|
+
}
|
|
1380
|
+
function createWavefrontReferenceRay(config, options = {}) {
|
|
1381
|
+
if (!config || typeof config !== "object") {
|
|
1382
|
+
throw new Error("config must be a wavefront path tracing config.");
|
|
1383
|
+
}
|
|
1384
|
+
const tile = normalizeReferenceTile(config, options.tile);
|
|
1385
|
+
const tilePixelCount = tile.width * tile.height;
|
|
1386
|
+
const pixelIndex = readNonNegativeInteger("pixelIndex", options.pixelIndex, 0);
|
|
1387
|
+
if (pixelIndex >= tilePixelCount) {
|
|
1388
|
+
throw new Error(`pixelIndex ${pixelIndex} exceeds tile capacity ${tilePixelCount}.`);
|
|
1389
|
+
}
|
|
1390
|
+
const sampleIndex = readNonNegativeInteger("sampleIndex", options.sampleIndex, 0);
|
|
1391
|
+
const frameIndex = readNonNegativeInteger("frameIndex", options.frameIndex, config.frameIndex ?? 0);
|
|
1392
|
+
const jitterScale = clamp(readFiniteNumber("jitterScale", options.jitterScale, 0.35), 0, 1);
|
|
1393
|
+
const localX = pixelIndex % tile.width;
|
|
1394
|
+
const localY = Math.floor(pixelIndex / tile.width);
|
|
1395
|
+
const pixelX = tile.x + localX;
|
|
1396
|
+
const pixelY = tile.y + localY;
|
|
1397
|
+
const sourcePixelId = pixelY * config.width + pixelX;
|
|
1398
|
+
const jitterX = random01FromSeed(mixSeed(sourcePixelId, sampleIndex, 0, frameIndex, 1)) - 0.5;
|
|
1399
|
+
const jitterY = random01FromSeed(mixSeed(sourcePixelId, sampleIndex, 0, frameIndex, 2)) - 0.5;
|
|
1400
|
+
const ndcX = (pixelX + 0.5 + jitterX * jitterScale) / config.width * 2 - 1;
|
|
1401
|
+
const ndcY = 1 - (pixelY + 0.5 + jitterY * jitterScale) / config.height * 2;
|
|
1402
|
+
const viewX = ndcX * config.camera.tanHalfFovY * config.camera.aspect;
|
|
1403
|
+
const viewY = ndcY * config.camera.tanHalfFovY;
|
|
1404
|
+
const direction = normalize(
|
|
1405
|
+
add(
|
|
1406
|
+
add(config.camera.forward, scale(config.camera.right, viewX)),
|
|
1407
|
+
scale(config.camera.up, viewY)
|
|
1408
|
+
),
|
|
1409
|
+
config.camera.forward
|
|
1410
|
+
);
|
|
1411
|
+
return Object.freeze({
|
|
1412
|
+
rayId: pixelIndex,
|
|
1413
|
+
parentRayId: 4294967295,
|
|
1414
|
+
sourcePixelId,
|
|
1415
|
+
sampleId: sampleIndex,
|
|
1416
|
+
bounce: 0,
|
|
1417
|
+
mediumRefId: 0,
|
|
1418
|
+
flags: 0,
|
|
1419
|
+
origin: Object.freeze([...config.camera.position]),
|
|
1420
|
+
direction: Object.freeze(direction),
|
|
1421
|
+
throughput: Object.freeze([1, 1, 1, 1]),
|
|
1422
|
+
pixelX,
|
|
1423
|
+
pixelY
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
function intersectWavefrontReferenceTriangle(ray, triangle, options = {}) {
|
|
1427
|
+
if (!ray || typeof ray !== "object") {
|
|
1428
|
+
throw new Error("ray must be a wavefront reference ray.");
|
|
1429
|
+
}
|
|
1430
|
+
if (!triangle || typeof triangle !== "object") {
|
|
1431
|
+
throw new Error("triangle must be a wavefront triangle record.");
|
|
1432
|
+
}
|
|
1433
|
+
const maxDistance = readOptionalMaxDistance(options.maxDistance);
|
|
1434
|
+
const triangleIndex = readNonNegativeInteger("triangleIndex", options.triangleIndex, 0);
|
|
1435
|
+
const edge1 = subtract(triangle.v1, triangle.v0);
|
|
1436
|
+
const edge2 = subtract(triangle.v2, triangle.v0);
|
|
1437
|
+
const pvec = cross(ray.direction, edge2);
|
|
1438
|
+
const determinant = dot(edge1, pvec);
|
|
1439
|
+
if (Math.abs(determinant) < 1e-7) {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
const invDet = 1 / determinant;
|
|
1443
|
+
const tvec = subtract(ray.origin, triangle.v0);
|
|
1444
|
+
const u = dot(tvec, pvec) * invDet;
|
|
1445
|
+
if (u < 0 || u > 1) {
|
|
1446
|
+
return null;
|
|
1447
|
+
}
|
|
1448
|
+
const qvec = cross(tvec, edge1);
|
|
1449
|
+
const v = dot(ray.direction, qvec) * invDet;
|
|
1450
|
+
if (v < 0 || u + v > 1) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
const distance = dot(edge2, qvec) * invDet;
|
|
1454
|
+
if (distance <= 1e-3 || distance > maxDistance) {
|
|
1455
|
+
return null;
|
|
1456
|
+
}
|
|
1457
|
+
const geometric = normalize(cross(edge1, edge2), [0, 1, 0]);
|
|
1458
|
+
const frontFace = dot(ray.direction, geometric) < 0;
|
|
1459
|
+
const orientedGeometric = frontFace ? geometric : scale(geometric, -1);
|
|
1460
|
+
const w = 1 - u - v;
|
|
1461
|
+
const interpolated = [
|
|
1462
|
+
triangle.n0[0] * w + triangle.n1[0] * u + triangle.n2[0] * v,
|
|
1463
|
+
triangle.n0[1] * w + triangle.n1[1] * u + triangle.n2[1] * v,
|
|
1464
|
+
triangle.n0[2] * w + triangle.n1[2] * u + triangle.n2[2] * v
|
|
1465
|
+
];
|
|
1466
|
+
const shadingNormal = repairReferenceShadingNormal(orientedGeometric, interpolated);
|
|
1467
|
+
const uv = [
|
|
1468
|
+
triangle.uv0[0] * w + triangle.uv1[0] * u + triangle.uv2[0] * v,
|
|
1469
|
+
triangle.uv0[1] * w + triangle.uv1[1] * u + triangle.uv2[1] * v
|
|
1470
|
+
];
|
|
1471
|
+
const position = add(ray.origin, scale(ray.direction, distance));
|
|
1472
|
+
return Object.freeze({
|
|
1473
|
+
hitType: "surface",
|
|
1474
|
+
rayId: ray.rayId,
|
|
1475
|
+
sourcePixelId: ray.sourcePixelId,
|
|
1476
|
+
distance,
|
|
1477
|
+
entityId: triangle.meshId,
|
|
1478
|
+
instanceId: 0,
|
|
1479
|
+
primitiveId: triangle.triangleId,
|
|
1480
|
+
materialId: triangle.materialKind,
|
|
1481
|
+
materialRefId: triangle.materialRefId,
|
|
1482
|
+
mediumRefId: triangle.mediumRefId,
|
|
1483
|
+
barycentrics: Object.freeze([w, u, v]),
|
|
1484
|
+
uv: Object.freeze(uv),
|
|
1485
|
+
geometricNormal: Object.freeze(orientedGeometric),
|
|
1486
|
+
shadingNormal: Object.freeze(shadingNormal),
|
|
1487
|
+
frontFace,
|
|
1488
|
+
triangleIndex,
|
|
1489
|
+
triangleId: triangle.triangleId,
|
|
1490
|
+
position: Object.freeze(position),
|
|
1491
|
+
color: triangle.color,
|
|
1492
|
+
emission: triangle.emission,
|
|
1493
|
+
material: triangle.material
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
function createWavefrontReferenceEnvironmentHit(config, ray) {
|
|
1497
|
+
const radiance = evaluateReferenceEnvironmentRadiance(config, ray.origin, ray.direction);
|
|
1498
|
+
return Object.freeze({
|
|
1499
|
+
hitType: "environment",
|
|
1500
|
+
rayId: ray.rayId,
|
|
1501
|
+
sourcePixelId: ray.sourcePixelId,
|
|
1502
|
+
distance: -1,
|
|
1503
|
+
entityId: 0,
|
|
1504
|
+
instanceId: 0,
|
|
1505
|
+
primitiveId: 0,
|
|
1506
|
+
materialId: 0,
|
|
1507
|
+
materialRefId: 0,
|
|
1508
|
+
mediumRefId: 0,
|
|
1509
|
+
barycentrics: Object.freeze([0, 0, 0]),
|
|
1510
|
+
uv: Object.freeze([0, 0]),
|
|
1511
|
+
geometricNormal: Object.freeze(scale(ray.direction, -1)),
|
|
1512
|
+
shadingNormal: Object.freeze(scale(ray.direction, -1)),
|
|
1513
|
+
frontFace: true,
|
|
1514
|
+
triangleIndex: -1,
|
|
1515
|
+
triangleId: -1,
|
|
1516
|
+
position: Object.freeze(add(ray.origin, scale(ray.direction, 1e3))),
|
|
1517
|
+
color: Object.freeze([0, 0, 0, 0]),
|
|
1518
|
+
emission: radiance,
|
|
1519
|
+
material: Object.freeze([1, 0, 1, 1])
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
function traceWavefrontReferenceTriangles(config, ray, triangles, options = {}) {
|
|
1523
|
+
if (!config || typeof config !== "object") {
|
|
1524
|
+
throw new Error("config must be a wavefront path tracing config.");
|
|
1525
|
+
}
|
|
1526
|
+
const source = Array.isArray(triangles) ? triangles : [];
|
|
1527
|
+
let nearestHit = null;
|
|
1528
|
+
let nearestDistance = readOptionalMaxDistance(options.maxDistance);
|
|
1529
|
+
source.forEach((triangle, index) => {
|
|
1530
|
+
const hit = intersectWavefrontReferenceTriangle(ray, triangle, {
|
|
1531
|
+
maxDistance: Number.isFinite(nearestDistance) ? nearestDistance : void 0,
|
|
1532
|
+
triangleIndex: index
|
|
1533
|
+
});
|
|
1534
|
+
if (hit && hit.distance < nearestDistance) {
|
|
1535
|
+
nearestDistance = hit.distance;
|
|
1536
|
+
nearestHit = hit;
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
return nearestHit ?? createWavefrontReferenceEnvironmentHit(config, ray);
|
|
1540
|
+
}
|
|
1296
1541
|
function clampTileSizeForDevice(config, device) {
|
|
1297
1542
|
const limit = Number(device?.limits?.maxStorageBufferBindingSize);
|
|
1298
1543
|
if (!Number.isFinite(limit) || limit <= 0) {
|
|
@@ -4831,11 +5076,13 @@ export {
|
|
|
4831
5076
|
createWavefrontPathTracingComputeConfig,
|
|
4832
5077
|
createWavefrontPathTracingComputeRenderer,
|
|
4833
5078
|
createWavefrontPathTracingPlan,
|
|
5079
|
+
createWavefrontReferenceRay,
|
|
4834
5080
|
defaultRendererClearColor,
|
|
4835
5081
|
defaultRendererWorkerProfile,
|
|
4836
5082
|
estimateWavefrontPathTracingMemory,
|
|
4837
5083
|
getRendererWorkerManifest,
|
|
4838
5084
|
getRendererWorkerProfile,
|
|
5085
|
+
intersectWavefrontReferenceTriangle,
|
|
4839
5086
|
normalizeWavefrontMesh,
|
|
4840
5087
|
normalizeWavefrontSceneObject,
|
|
4841
5088
|
packWavefrontBvhNodes,
|
|
@@ -4855,6 +5102,7 @@ export {
|
|
|
4855
5102
|
rendererWorkerQueueClass,
|
|
4856
5103
|
supportsWavefrontPathTracingCompute,
|
|
4857
5104
|
supportsWebGpu,
|
|
5105
|
+
traceWavefrontReferenceTriangles,
|
|
4858
5106
|
wavefrontMaterialKinds,
|
|
4859
5107
|
wavefrontPathTracingComputeLimits,
|
|
4860
5108
|
wavefrontSceneObjectKinds
|