@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/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