@plasius/gpu-renderer 0.2.6 → 0.2.7
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 +6 -2
- package/README.md +9 -0
- package/dist/index.cjs +414 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +414 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +56 -4
- package/src/wavefront-compute.js +465 -54
package/dist/index.js
CHANGED
|
@@ -138,17 +138,19 @@ var DEFAULT_MAX_DEPTH = 6;
|
|
|
138
138
|
var DEFAULT_TILE_SIZE = 128;
|
|
139
139
|
var DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
140
140
|
var MAX_SAMPLES_PER_PIXEL = 256;
|
|
141
|
-
var DEFAULT_BRDF_LUT_SIZE =
|
|
141
|
+
var DEFAULT_BRDF_LUT_SIZE = 128;
|
|
142
|
+
var DEFAULT_BRDF_LUT_SAMPLE_COUNT = 256;
|
|
142
143
|
var DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
|
|
143
144
|
var DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
144
145
|
var DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
146
|
+
var DEFAULT_MEDIUM_PHASE_MODEL = 0;
|
|
145
147
|
var WORKGROUP_SIZE = 64;
|
|
146
148
|
var rendererWavefrontComputeMode = "webgpu-compute";
|
|
147
149
|
var rendererWavefrontComputeWorkgroupSize = WORKGROUP_SIZE;
|
|
148
150
|
var rendererWavefrontComputeStatsStride = 8;
|
|
149
151
|
var RAY_RECORD_BYTES = 80;
|
|
150
152
|
var HIT_RECORD_BYTES = 256;
|
|
151
|
-
var SCENE_OBJECT_RECORD_BYTES =
|
|
153
|
+
var SCENE_OBJECT_RECORD_BYTES = 160;
|
|
152
154
|
var MESH_VERTEX_RECORD_BYTES = 48;
|
|
153
155
|
var MESH_RANGE_RECORD_BYTES = 240;
|
|
154
156
|
var TRIANGLE_RECORD_BYTES = 352;
|
|
@@ -157,6 +159,7 @@ var BVH_NODE_RECORD_BYTES = 48;
|
|
|
157
159
|
var BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
158
160
|
var EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
159
161
|
var ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
162
|
+
var MEDIUM_TABLE_ROWS = 2;
|
|
160
163
|
var ACCUMULATION_RECORD_BYTES = 16;
|
|
161
164
|
var PATH_VERTEX_RECORD_BYTES = 16;
|
|
162
165
|
var GPU_SUBMITTED_WORK_TIMEOUT_MS = 5e3;
|
|
@@ -503,6 +506,156 @@ function deriveBounds(input) {
|
|
|
503
506
|
}
|
|
504
507
|
return null;
|
|
505
508
|
}
|
|
509
|
+
function deriveBeerLambertAbsorptionFromAttenuationColor(attenuationColor, attenuationDistance, density = 1) {
|
|
510
|
+
const distance = Number(attenuationDistance);
|
|
511
|
+
const densityScale = Math.max(0, Number(density) || 0);
|
|
512
|
+
if (!Number.isFinite(distance) || distance <= 0 || densityScale <= 0) {
|
|
513
|
+
return [0, 0, 0];
|
|
514
|
+
}
|
|
515
|
+
return attenuationColor.slice(0, 3).map((channel) => {
|
|
516
|
+
const clamped = clamp(Number(channel) || 0, 1e-4, 1);
|
|
517
|
+
return Math.max(0, -Math.log(clamped) / distance * densityScale);
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
function readMediumPhaseModel(value) {
|
|
521
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
522
|
+
return Math.max(0, Math.trunc(value));
|
|
523
|
+
}
|
|
524
|
+
switch (String(value ?? "").trim().toLowerCase()) {
|
|
525
|
+
case "isotropic":
|
|
526
|
+
default:
|
|
527
|
+
return DEFAULT_MEDIUM_PHASE_MODEL;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function resolveWavefrontVolumeInput(input) {
|
|
531
|
+
return input?.volume ?? input?.material?.volume ?? null;
|
|
532
|
+
}
|
|
533
|
+
function normalizeWavefrontThickness(input, label) {
|
|
534
|
+
const volume = resolveWavefrontVolumeInput(input);
|
|
535
|
+
return Math.max(
|
|
536
|
+
0,
|
|
537
|
+
readFiniteNumber(
|
|
538
|
+
label,
|
|
539
|
+
input?.thickness ?? volume?.thickness ?? input?.material?.thickness,
|
|
540
|
+
0
|
|
541
|
+
)
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
function resolveWavefrontMediumId(input, fallbackId = 1) {
|
|
545
|
+
return input?.mediumRefId ?? input?.mediumId ?? input?.material?.mediumId ?? input?.materialRefId ?? input?.material?.id ?? input?.materialId ?? input?.id ?? fallbackId;
|
|
546
|
+
}
|
|
547
|
+
function deriveWavefrontTransportMedium(input, fallbackId = 1) {
|
|
548
|
+
const resolvedId = resolveWavefrontMediumId(input, fallbackId);
|
|
549
|
+
if (input?.medium) {
|
|
550
|
+
return normalizeWavefrontMedium(
|
|
551
|
+
{
|
|
552
|
+
...input.medium,
|
|
553
|
+
id: input.medium.id ?? input.medium.mediumId ?? resolvedId
|
|
554
|
+
},
|
|
555
|
+
fallbackId
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
const volume = resolveWavefrontVolumeInput(input);
|
|
559
|
+
if (!volume) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
return normalizeWavefrontMedium(
|
|
563
|
+
{
|
|
564
|
+
id: resolvedId,
|
|
565
|
+
phaseModel: volume.phaseModel,
|
|
566
|
+
density: volume.density,
|
|
567
|
+
attenuationColor: volume.attenuationColor,
|
|
568
|
+
attenuationDistance: volume.attenuationDistance,
|
|
569
|
+
absorption: volume.absorption,
|
|
570
|
+
scattering: volume.scattering
|
|
571
|
+
},
|
|
572
|
+
fallbackId
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
function normalizeWavefrontMedium(input = {}, index = 0) {
|
|
576
|
+
const id = readNonNegativeInteger("medium id", input.id ?? input.mediumId, index);
|
|
577
|
+
const density = Math.max(0, readFiniteNumber("medium density", input.density, 1));
|
|
578
|
+
const attenuationColor = asColor(
|
|
579
|
+
input.attenuationColor ?? input.color ?? input.medium?.attenuationColor,
|
|
580
|
+
[1, 1, 1, 1]
|
|
581
|
+
);
|
|
582
|
+
const attenuationDistance = readFiniteNumber(
|
|
583
|
+
"medium attenuationDistance",
|
|
584
|
+
input.attenuationDistance ?? input.distance ?? input.medium?.attenuationDistance,
|
|
585
|
+
0
|
|
586
|
+
);
|
|
587
|
+
const absorption = Array.isArray(input.absorption) || Array.isArray(input.medium?.absorption) ? asVec3(input.absorption ?? input.medium?.absorption, [0, 0, 0]).map(
|
|
588
|
+
(value) => Math.max(0, Number(value) || 0)
|
|
589
|
+
) : deriveBeerLambertAbsorptionFromAttenuationColor(
|
|
590
|
+
attenuationColor,
|
|
591
|
+
attenuationDistance,
|
|
592
|
+
density
|
|
593
|
+
);
|
|
594
|
+
const scattering = asVec3(
|
|
595
|
+
input.scattering ?? input.medium?.scattering,
|
|
596
|
+
[0, 0, 0]
|
|
597
|
+
).map((value) => Math.max(0, Number(value) || 0));
|
|
598
|
+
return Object.freeze({
|
|
599
|
+
id,
|
|
600
|
+
phaseModel: readMediumPhaseModel(input.phaseModel ?? input.medium?.phaseModel),
|
|
601
|
+
density,
|
|
602
|
+
attenuationColor: Object.freeze(attenuationColor),
|
|
603
|
+
attenuationDistance,
|
|
604
|
+
absorption: Object.freeze(absorption),
|
|
605
|
+
scattering: Object.freeze(scattering)
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
function collectWavefrontMediums(options, meshes, sceneObjects = []) {
|
|
609
|
+
const mediumsById = /* @__PURE__ */ new Map();
|
|
610
|
+
mediumsById.set(
|
|
611
|
+
0,
|
|
612
|
+
Object.freeze({
|
|
613
|
+
id: 0,
|
|
614
|
+
phaseModel: DEFAULT_MEDIUM_PHASE_MODEL,
|
|
615
|
+
density: 0,
|
|
616
|
+
attenuationColor: Object.freeze([1, 1, 1, 1]),
|
|
617
|
+
attenuationDistance: 0,
|
|
618
|
+
absorption: Object.freeze([0, 0, 0]),
|
|
619
|
+
scattering: Object.freeze([0, 0, 0])
|
|
620
|
+
})
|
|
621
|
+
);
|
|
622
|
+
const register = (input, fallbackId = mediumsById.size) => {
|
|
623
|
+
if (!input) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const normalized = normalizeWavefrontMedium(
|
|
627
|
+
typeof input === "object" ? { id: fallbackId, ...input } : { id: fallbackId },
|
|
628
|
+
fallbackId
|
|
629
|
+
);
|
|
630
|
+
const existing = mediumsById.get(normalized.id);
|
|
631
|
+
if (existing && JSON.stringify(existing) !== JSON.stringify(normalized)) {
|
|
632
|
+
throw new Error(`Medium id ${normalized.id} is defined more than once with different values.`);
|
|
633
|
+
}
|
|
634
|
+
mediumsById.set(normalized.id, normalized);
|
|
635
|
+
};
|
|
636
|
+
for (const medium of options.mediums ?? []) {
|
|
637
|
+
register(medium);
|
|
638
|
+
}
|
|
639
|
+
for (const mesh of meshes) {
|
|
640
|
+
register(mesh.medium, mesh.mediumRefId ?? mesh.medium?.id ?? 0);
|
|
641
|
+
}
|
|
642
|
+
for (const mesh of meshes) {
|
|
643
|
+
if ((mesh.mediumRefId ?? 0) > 0 && !mediumsById.has(mesh.mediumRefId)) {
|
|
644
|
+
register({ id: mesh.mediumRefId });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const object of sceneObjects) {
|
|
648
|
+
register(object.medium, object.mediumRefId ?? object.medium?.id ?? 0);
|
|
649
|
+
}
|
|
650
|
+
for (const object of sceneObjects) {
|
|
651
|
+
if ((object.mediumRefId ?? 0) > 0 && !mediumsById.has(object.mediumRefId)) {
|
|
652
|
+
register({ id: object.mediumRefId });
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return Object.freeze(
|
|
656
|
+
Array.from(mediumsById.values()).sort((left, right) => left.id - right.id)
|
|
657
|
+
);
|
|
658
|
+
}
|
|
506
659
|
function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
507
660
|
const bounds = deriveBounds(input);
|
|
508
661
|
const kind = readObjectKind(input.kind ?? input.type ?? (bounds ? "box" : "sphere"));
|
|
@@ -533,12 +686,19 @@ function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
|
533
686
|
input.specularColor ?? input.material?.specularColor,
|
|
534
687
|
[1, 1, 1, 1]
|
|
535
688
|
).map((value, componentIndex) => componentIndex < 3 ? clamp(value, 0, 1) : 1);
|
|
689
|
+
const medium = deriveWavefrontTransportMedium(input, index + 1);
|
|
536
690
|
const resolvedMaterialKind = emission[0] > 0 || emission[1] > 0 || emission[2] > 0 ? MATERIAL_EMISSIVE : materialKindInput === void 0 || materialKindInput === null ? transmission > 1e-3 || opacity < 0.999 ? MATERIAL_TRANSPARENT : materialKind : materialKind;
|
|
537
691
|
return Object.freeze({
|
|
538
692
|
id: readNonNegativeInteger("id", input.id, index + 1),
|
|
539
693
|
kind,
|
|
540
694
|
materialKind: resolvedMaterialKind,
|
|
541
695
|
flags: readNonNegativeInteger("flags", input.flags, 0),
|
|
696
|
+
mediumRefId: readNonNegativeInteger(
|
|
697
|
+
"mediumRefId",
|
|
698
|
+
input.mediumRefId ?? medium?.id ?? input.medium?.id ?? input.mediumId,
|
|
699
|
+
0
|
|
700
|
+
),
|
|
701
|
+
medium,
|
|
542
702
|
center: Object.freeze(center),
|
|
543
703
|
halfExtent: Object.freeze(halfExtent),
|
|
544
704
|
color: Object.freeze(color),
|
|
@@ -562,6 +722,7 @@ function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
|
562
722
|
),
|
|
563
723
|
specular: clamp(readFiniteNumber("specular", input.specular ?? input.material?.specular, 1), 0, 1),
|
|
564
724
|
specularColor: Object.freeze(specularColor),
|
|
725
|
+
thickness: normalizeWavefrontThickness(input, "thickness"),
|
|
565
726
|
transmission
|
|
566
727
|
});
|
|
567
728
|
}
|
|
@@ -655,6 +816,7 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
655
816
|
input.specularColor ?? input.material?.specularColor,
|
|
656
817
|
[1, 1, 1, 1]
|
|
657
818
|
).map((value, componentIndex) => componentIndex < 3 ? clamp(value, 0, 1) : 1);
|
|
819
|
+
const medium = deriveWavefrontTransportMedium(input, meshIndex + 1);
|
|
658
820
|
const resolvedMaterialKind = emission[0] > 0 || emission[1] > 0 || emission[2] > 0 ? MATERIAL_EMISSIVE : materialKindInput === void 0 || materialKindInput === null ? transmission > 1e-3 || opacity < 0.999 ? MATERIAL_TRANSPARENT : materialKind : materialKind;
|
|
659
821
|
return Object.freeze({
|
|
660
822
|
id: readNonNegativeInteger("mesh id", input.id, meshIndex + 1),
|
|
@@ -671,9 +833,10 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
671
833
|
),
|
|
672
834
|
mediumRefId: readNonNegativeInteger(
|
|
673
835
|
"mesh mediumRefId",
|
|
674
|
-
input.mediumRefId ?? input.medium?.id ?? input.mediumId,
|
|
836
|
+
input.mediumRefId ?? medium?.id ?? input.medium?.id ?? input.mediumId ?? input.material?.mediumId,
|
|
675
837
|
0
|
|
676
838
|
),
|
|
839
|
+
medium,
|
|
677
840
|
color: Object.freeze(color),
|
|
678
841
|
emission: Object.freeze(emission),
|
|
679
842
|
roughness: clamp(readFiniteNumber("roughness", input.roughness ?? input.material?.roughness, 0.72), 0, 1),
|
|
@@ -695,6 +858,7 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
695
858
|
),
|
|
696
859
|
specular: clamp(readFiniteNumber("specular", input.specular ?? input.material?.specular, 1), 0, 1),
|
|
697
860
|
specularColor: Object.freeze(specularColor),
|
|
861
|
+
thickness: normalizeWavefrontThickness(input, "mesh thickness"),
|
|
698
862
|
transmission,
|
|
699
863
|
baseColorTexture: input.baseColorTexture ?? input.material?.baseColorTexture ?? null,
|
|
700
864
|
metallicRoughnessTexture: input.metallicRoughnessTexture ?? input.material?.metallicRoughnessTexture ?? null,
|
|
@@ -906,7 +1070,7 @@ function createMeshTriangleRecords(meshes) {
|
|
|
906
1070
|
mesh.clearcoatRoughness,
|
|
907
1071
|
mesh.specular,
|
|
908
1072
|
mesh.transmission,
|
|
909
|
-
|
|
1073
|
+
mesh.thickness
|
|
910
1074
|
]),
|
|
911
1075
|
specularColor: Object.freeze([
|
|
912
1076
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1200,7 +1364,7 @@ function createWavefrontGpuMaterialSource(meshes = []) {
|
|
|
1200
1364
|
mesh.clearcoatRoughness,
|
|
1201
1365
|
mesh.specular,
|
|
1202
1366
|
mesh.transmission,
|
|
1203
|
-
|
|
1367
|
+
mesh.thickness
|
|
1204
1368
|
]);
|
|
1205
1369
|
writeVec4(floatView, byteOffset + 80, [
|
|
1206
1370
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1368,7 +1532,7 @@ function createWavefrontGpuMeshSource(meshes = [], gpuMaterialSourceInput = null
|
|
|
1368
1532
|
mesh.clearcoatRoughness,
|
|
1369
1533
|
mesh.specular,
|
|
1370
1534
|
mesh.transmission,
|
|
1371
|
-
|
|
1535
|
+
mesh.thickness
|
|
1372
1536
|
]);
|
|
1373
1537
|
writeVec4(meshFloats, floatOffset * 4 + 128, [
|
|
1374
1538
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1469,12 +1633,16 @@ function normalizeSceneObjects(sceneObjects, useDefaultScene = true) {
|
|
|
1469
1633
|
const source = Array.isArray(sceneObjects) && sceneObjects.length > 0 ? sceneObjects : useDefaultScene ? createDefaultWavefrontSceneObjects() : [];
|
|
1470
1634
|
return source.map((object, index) => normalizeWavefrontSceneObject(object, index));
|
|
1471
1635
|
}
|
|
1636
|
+
function normalizeWavefrontMeshes(meshes) {
|
|
1637
|
+
const source = Array.isArray(meshes) ? meshes : [];
|
|
1638
|
+
return source.map((mesh, index) => normalizeWavefrontMesh(mesh, index));
|
|
1639
|
+
}
|
|
1472
1640
|
function normalizeMeshes(options = {}) {
|
|
1473
1641
|
if (Array.isArray(options.meshes)) {
|
|
1474
|
-
return options.meshes;
|
|
1642
|
+
return normalizeWavefrontMeshes(options.meshes);
|
|
1475
1643
|
}
|
|
1476
1644
|
if (options.mesh) {
|
|
1477
|
-
return [options.mesh];
|
|
1645
|
+
return normalizeWavefrontMeshes([options.mesh]);
|
|
1478
1646
|
}
|
|
1479
1647
|
return [];
|
|
1480
1648
|
}
|
|
@@ -1781,6 +1949,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1781
1949
|
const sceneObjects = Object.freeze(
|
|
1782
1950
|
normalizeSceneObjects(options.sceneObjects, meshes.length === 0)
|
|
1783
1951
|
);
|
|
1952
|
+
const mediums = collectWavefrontMediums(options, meshes, sceneObjects);
|
|
1784
1953
|
const sceneObjectCapacity = Math.max(
|
|
1785
1954
|
sceneObjects.length,
|
|
1786
1955
|
readPositiveInteger("sceneObjectCapacity", options.sceneObjectCapacity, DEFAULT_SCENE_OBJECT_CAPACITY)
|
|
@@ -1839,6 +2008,8 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1839
2008
|
sceneObjects,
|
|
1840
2009
|
sceneObjectCount: sceneObjects.length,
|
|
1841
2010
|
sceneObjectCapacity,
|
|
2011
|
+
mediums,
|
|
2012
|
+
mediumCount: mediums.length,
|
|
1842
2013
|
accelerationBuildMode,
|
|
1843
2014
|
gpuAccelerationBuildRequired: accelerationBuildMode === "gpu" && triangleCount > 0,
|
|
1844
2015
|
gpuMeshSource,
|
|
@@ -1940,29 +2111,30 @@ function packWavefrontSceneObjects(sceneObjects, capacity = sceneObjects.length)
|
|
|
1940
2111
|
uintView[u32 + 1] = object.id;
|
|
1941
2112
|
uintView[u32 + 2] = object.materialKind;
|
|
1942
2113
|
uintView[u32 + 3] = object.flags;
|
|
1943
|
-
|
|
1944
|
-
writeVec4(floatView, byteOffset + 32, [...object.
|
|
1945
|
-
writeVec4(floatView, byteOffset + 48, object.
|
|
1946
|
-
writeVec4(floatView, byteOffset + 64, object.
|
|
1947
|
-
writeVec4(floatView, byteOffset + 80,
|
|
2114
|
+
uintView[u32 + 4] = object.mediumRefId;
|
|
2115
|
+
writeVec4(floatView, byteOffset + 32, [...object.center, 0]);
|
|
2116
|
+
writeVec4(floatView, byteOffset + 48, [...object.halfExtent, 0]);
|
|
2117
|
+
writeVec4(floatView, byteOffset + 64, object.color);
|
|
2118
|
+
writeVec4(floatView, byteOffset + 80, object.emission);
|
|
2119
|
+
writeVec4(floatView, byteOffset + 96, [
|
|
1948
2120
|
object.roughness,
|
|
1949
2121
|
object.metallic,
|
|
1950
2122
|
object.opacity,
|
|
1951
2123
|
object.ior
|
|
1952
2124
|
]);
|
|
1953
|
-
writeVec4(floatView, byteOffset +
|
|
2125
|
+
writeVec4(floatView, byteOffset + 112, [
|
|
1954
2126
|
object.sheenColor[0] ?? 0,
|
|
1955
2127
|
object.sheenColor[1] ?? 0,
|
|
1956
2128
|
object.sheenColor[2] ?? 0,
|
|
1957
2129
|
object.clearcoat
|
|
1958
2130
|
]);
|
|
1959
|
-
writeVec4(floatView, byteOffset +
|
|
2131
|
+
writeVec4(floatView, byteOffset + 128, [
|
|
1960
2132
|
object.clearcoatRoughness,
|
|
1961
2133
|
object.specular,
|
|
1962
2134
|
object.transmission,
|
|
1963
|
-
|
|
2135
|
+
object.thickness
|
|
1964
2136
|
]);
|
|
1965
|
-
writeVec4(floatView, byteOffset +
|
|
2137
|
+
writeVec4(floatView, byteOffset + 144, [
|
|
1966
2138
|
object.specularColor[0] ?? 1,
|
|
1967
2139
|
object.specularColor[1] ?? 1,
|
|
1968
2140
|
object.specularColor[2] ?? 1,
|
|
@@ -2498,7 +2670,7 @@ function integrateBrdfSample(nDotV, roughness, sampleCount) {
|
|
|
2498
2670
|
}
|
|
2499
2671
|
return [scaleTerm / sampleCount, biasTerm / sampleCount];
|
|
2500
2672
|
}
|
|
2501
|
-
function createBrdfLutUploadBytes(size = DEFAULT_BRDF_LUT_SIZE, sampleCount =
|
|
2673
|
+
function createBrdfLutUploadBytes(size = DEFAULT_BRDF_LUT_SIZE, sampleCount = DEFAULT_BRDF_LUT_SAMPLE_COUNT) {
|
|
2502
2674
|
const cacheKey = `${Math.max(1, Math.trunc(size))}:${Math.max(1, Math.trunc(sampleCount))}`;
|
|
2503
2675
|
const cached = BRDF_LUT_UPLOAD_CACHE.get(cacheKey);
|
|
2504
2676
|
if (cached) {
|
|
@@ -2870,6 +3042,80 @@ function createBrdfLutResource(device, constants, size = DEFAULT_BRDF_LUT_SIZE)
|
|
|
2870
3042
|
height: upload.height
|
|
2871
3043
|
});
|
|
2872
3044
|
}
|
|
3045
|
+
function createMediumTextureResource(device, constants, mediums) {
|
|
3046
|
+
const normalized = Array.isArray(mediums) && mediums.length > 0 ? mediums : [{ id: 0 }];
|
|
3047
|
+
const width = Math.max(
|
|
3048
|
+
1,
|
|
3049
|
+
normalized.reduce((maximum, medium) => Math.max(maximum, medium.id ?? 0), 0) + 1
|
|
3050
|
+
);
|
|
3051
|
+
const level = {
|
|
3052
|
+
width,
|
|
3053
|
+
height: MEDIUM_TABLE_ROWS,
|
|
3054
|
+
data: new Float32Array(width * MEDIUM_TABLE_ROWS * 4)
|
|
3055
|
+
};
|
|
3056
|
+
for (const medium of normalized) {
|
|
3057
|
+
const mediumId = Math.max(0, Math.trunc(Number(medium.id) || 0));
|
|
3058
|
+
const absorptionOffset = mediumId * 4;
|
|
3059
|
+
level.data[absorptionOffset] = Math.max(0, medium.absorption?.[0] ?? 0);
|
|
3060
|
+
level.data[absorptionOffset + 1] = Math.max(0, medium.absorption?.[1] ?? 0);
|
|
3061
|
+
level.data[absorptionOffset + 2] = Math.max(0, medium.absorption?.[2] ?? 0);
|
|
3062
|
+
level.data[absorptionOffset + 3] = Math.max(0, medium.phaseModel ?? 0);
|
|
3063
|
+
const scatteringOffset = (width + mediumId) * 4;
|
|
3064
|
+
level.data[scatteringOffset] = Math.max(0, medium.scattering?.[0] ?? 0);
|
|
3065
|
+
level.data[scatteringOffset + 1] = Math.max(0, medium.scattering?.[1] ?? 0);
|
|
3066
|
+
level.data[scatteringOffset + 2] = Math.max(0, medium.scattering?.[2] ?? 0);
|
|
3067
|
+
level.data[scatteringOffset + 3] = Math.max(0, medium.density ?? 0);
|
|
3068
|
+
}
|
|
3069
|
+
const upload = createFloat16RgbaUploadFromLevels([level])[0];
|
|
3070
|
+
const texture = device.createTexture({
|
|
3071
|
+
label: "plasius.wavefront.mediumTable",
|
|
3072
|
+
size: { width, height: MEDIUM_TABLE_ROWS },
|
|
3073
|
+
format: "rgba16float",
|
|
3074
|
+
usage: constants.texture.TEXTURE_BINDING | constants.texture.COPY_DST
|
|
3075
|
+
});
|
|
3076
|
+
device.queue.writeTexture(
|
|
3077
|
+
{ texture },
|
|
3078
|
+
upload.bytes,
|
|
3079
|
+
{ bytesPerRow: upload.bytesPerRow, rowsPerImage: upload.height },
|
|
3080
|
+
{ width, height: MEDIUM_TABLE_ROWS, depthOrArrayLayers: 1 }
|
|
3081
|
+
);
|
|
3082
|
+
return Object.freeze({
|
|
3083
|
+
texture,
|
|
3084
|
+
view: texture.createView(),
|
|
3085
|
+
ownsTexture: true,
|
|
3086
|
+
count: normalized.length,
|
|
3087
|
+
width
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
function mediumTablesEqual(left, right) {
|
|
3091
|
+
const leftMediums = Array.isArray(left) ? left : [];
|
|
3092
|
+
const rightMediums = Array.isArray(right) ? right : [];
|
|
3093
|
+
if (leftMediums.length !== rightMediums.length) {
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
for (let index = 0; index < leftMediums.length; index += 1) {
|
|
3097
|
+
const leftMedium = leftMediums[index];
|
|
3098
|
+
const rightMedium = rightMediums[index];
|
|
3099
|
+
if ((leftMedium?.id ?? 0) !== (rightMedium?.id ?? 0)) {
|
|
3100
|
+
return false;
|
|
3101
|
+
}
|
|
3102
|
+
if ((leftMedium?.phaseModel ?? 0) !== (rightMedium?.phaseModel ?? 0)) {
|
|
3103
|
+
return false;
|
|
3104
|
+
}
|
|
3105
|
+
if ((leftMedium?.density ?? 0) !== (rightMedium?.density ?? 0)) {
|
|
3106
|
+
return false;
|
|
3107
|
+
}
|
|
3108
|
+
for (let component = 0; component < 3; component += 1) {
|
|
3109
|
+
if ((leftMedium?.absorption?.[component] ?? 0) !== (rightMedium?.absorption?.[component] ?? 0)) {
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
if ((leftMedium?.scattering?.[component] ?? 0) !== (rightMedium?.scattering?.[component] ?? 0)) {
|
|
3113
|
+
return false;
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
return true;
|
|
3118
|
+
}
|
|
2873
3119
|
function createAtlasTextureResource(device, constants, atlas, label) {
|
|
2874
3120
|
const upload = createRgba8TextureUpload(atlas);
|
|
2875
3121
|
const texture = device.createTexture({
|
|
@@ -3008,6 +3254,10 @@ struct SceneObject {
|
|
|
3008
3254
|
objectId: u32,
|
|
3009
3255
|
materialKind: u32,
|
|
3010
3256
|
flags: u32,
|
|
3257
|
+
mediumRefId: u32,
|
|
3258
|
+
pad0: u32,
|
|
3259
|
+
pad1: u32,
|
|
3260
|
+
pad2: u32,
|
|
3011
3261
|
center: vec4<f32>,
|
|
3012
3262
|
halfExtent: vec4<f32>,
|
|
3013
3263
|
color: vec4<f32>,
|
|
@@ -3068,9 +3318,9 @@ struct BvhLeafRef {
|
|
|
3068
3318
|
struct ScatterResult {
|
|
3069
3319
|
direction: vec4<f32>,
|
|
3070
3320
|
pdf: f32,
|
|
3321
|
+
mediumRefId: u32,
|
|
3071
3322
|
flags: u32,
|
|
3072
3323
|
pad0: u32,
|
|
3073
|
-
pad1: u32,
|
|
3074
3324
|
};
|
|
3075
3325
|
|
|
3076
3326
|
struct MeshVertex {
|
|
@@ -3216,6 +3466,7 @@ struct EnvironmentPortal {
|
|
|
3216
3466
|
@group(0) @binding(29) var brdfLutTexture: texture_2d<f32>;
|
|
3217
3467
|
@group(0) @binding(30) var brdfLutSampler: sampler;
|
|
3218
3468
|
@group(0) @binding(31) var environmentSamplingTexture: texture_2d<f32>;
|
|
3469
|
+
@group(0) @binding(32) var mediumTableTexture: texture_2d<f32>;
|
|
3219
3470
|
|
|
3220
3471
|
fn hash_u32(value: u32) -> u32 {
|
|
3221
3472
|
var x = value;
|
|
@@ -3881,6 +4132,60 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
3881
4132
|
return environment_radiance(origin, direction);
|
|
3882
4133
|
}
|
|
3883
4134
|
|
|
4135
|
+
fn medium_dimensions() -> vec2<u32> {
|
|
4136
|
+
return textureDimensions(mediumTableTexture);
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
fn medium_valid(mediumRefId: u32) -> bool {
|
|
4140
|
+
let dimensions = medium_dimensions();
|
|
4141
|
+
return mediumRefId > 0u && mediumRefId < dimensions.x;
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
fn medium_absorption(mediumRefId: u32) -> vec3<f32> {
|
|
4145
|
+
if (!medium_valid(mediumRefId)) {
|
|
4146
|
+
return vec3<f32>(0.0);
|
|
4147
|
+
}
|
|
4148
|
+
return max(
|
|
4149
|
+
textureLoad(mediumTableTexture, vec2<i32>(i32(mediumRefId), 0), 0).xyz,
|
|
4150
|
+
vec3<f32>(0.0)
|
|
4151
|
+
);
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
fn medium_scattering(mediumRefId: u32) -> vec3<f32> {
|
|
4155
|
+
if (!medium_valid(mediumRefId)) {
|
|
4156
|
+
return vec3<f32>(0.0);
|
|
4157
|
+
}
|
|
4158
|
+
return max(
|
|
4159
|
+
textureLoad(mediumTableTexture, vec2<i32>(i32(mediumRefId), 1), 0).xyz,
|
|
4160
|
+
vec3<f32>(0.0)
|
|
4161
|
+
);
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
fn medium_transmittance(mediumRefId: u32, distance: f32) -> vec3<f32> {
|
|
4165
|
+
if (!medium_valid(mediumRefId) || distance <= 0.000001) {
|
|
4166
|
+
return vec3<f32>(1.0);
|
|
4167
|
+
}
|
|
4168
|
+
let extinction = medium_absorption(mediumRefId) + medium_scattering(mediumRefId);
|
|
4169
|
+
return vec3<f32>(
|
|
4170
|
+
exp(-extinction.x * distance),
|
|
4171
|
+
exp(-extinction.y * distance),
|
|
4172
|
+
exp(-extinction.z * distance)
|
|
4173
|
+
);
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
fn transmitted_medium_ref_id(ray: RayRecord, hit: HitRecord) -> u32 {
|
|
4177
|
+
if (hit.mediumRefId == 0u) {
|
|
4178
|
+
return ray.mediumRefId;
|
|
4179
|
+
}
|
|
4180
|
+
if (hit.frontFace == 1u) {
|
|
4181
|
+
return hit.mediumRefId;
|
|
4182
|
+
}
|
|
4183
|
+
if (ray.mediumRefId == hit.mediumRefId) {
|
|
4184
|
+
return 0u;
|
|
4185
|
+
}
|
|
4186
|
+
return ray.mediumRefId;
|
|
4187
|
+
}
|
|
4188
|
+
|
|
3884
4189
|
fn surface_path_response(hit: HitRecord) -> vec3<f32> {
|
|
3885
4190
|
let color = clamp(hit.color.xyz, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
3886
4191
|
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
@@ -3979,11 +4284,15 @@ fn terminal_surface_environment_source(ray: RayRecord, hit: HitRecord) -> vec3<f
|
|
|
3979
4284
|
return clamp_sample_radiance(environmentFloor * materialFloor);
|
|
3980
4285
|
}
|
|
3981
4286
|
|
|
3982
|
-
fn terminal_surface_environment_contribution(
|
|
4287
|
+
fn terminal_surface_environment_contribution(
|
|
4288
|
+
ray: RayRecord,
|
|
4289
|
+
throughput: vec3<f32>,
|
|
4290
|
+
hit: HitRecord
|
|
4291
|
+
) -> vec3<f32> {
|
|
3983
4292
|
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
3984
4293
|
let occlusion = mix(0.75, 1.0, clamp(hit.occlusion, 0.0, 1.0));
|
|
3985
4294
|
return clamp_sample_radiance(
|
|
3986
|
-
|
|
4295
|
+
throughput *
|
|
3987
4296
|
surfaceColor *
|
|
3988
4297
|
terminal_surface_environment_source(ray, hit) *
|
|
3989
4298
|
occlusion
|
|
@@ -4457,7 +4766,7 @@ fn intersect_sphere(ray: RayRecord, object: SceneObject) -> Candidate {
|
|
|
4457
4766
|
0xffffffffu,
|
|
4458
4767
|
object.objectId,
|
|
4459
4768
|
object.objectId,
|
|
4460
|
-
|
|
4769
|
+
object.mediumRefId
|
|
4461
4770
|
);
|
|
4462
4771
|
}
|
|
4463
4772
|
|
|
@@ -4509,7 +4818,7 @@ fn intersect_box(ray: RayRecord, object: SceneObject) -> Candidate {
|
|
|
4509
4818
|
0xffffffffu,
|
|
4510
4819
|
object.objectId,
|
|
4511
4820
|
object.objectId,
|
|
4512
|
-
|
|
4821
|
+
object.mediumRefId
|
|
4513
4822
|
);
|
|
4514
4823
|
}
|
|
4515
4824
|
|
|
@@ -4760,6 +5069,10 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
4760
5069
|
let ray = activeQueue[index];
|
|
4761
5070
|
var nearest = 1000000.0;
|
|
4762
5071
|
var hitObject = SceneObject(
|
|
5072
|
+
0u,
|
|
5073
|
+
0u,
|
|
5074
|
+
0u,
|
|
5075
|
+
0u,
|
|
4763
5076
|
0u,
|
|
4764
5077
|
0u,
|
|
4765
5078
|
0u,
|
|
@@ -4963,9 +5276,9 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
4963
5276
|
return ScatterResult(
|
|
4964
5277
|
vec4<f32>(reflect(ray.direction.xyz, normal), 0.0),
|
|
4965
5278
|
1.0,
|
|
5279
|
+
ray.mediumRefId,
|
|
4966
5280
|
RAY_FLAG_DELTA_SAMPLE,
|
|
4967
5281
|
0u,
|
|
4968
|
-
0u
|
|
4969
5282
|
);
|
|
4970
5283
|
}
|
|
4971
5284
|
|
|
@@ -4985,17 +5298,17 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
4985
5298
|
return ScatterResult(
|
|
4986
5299
|
vec4<f32>(reflect(ray.direction.xyz, normal), 0.0),
|
|
4987
5300
|
1.0,
|
|
5301
|
+
ray.mediumRefId,
|
|
4988
5302
|
RAY_FLAG_DELTA_SAMPLE,
|
|
4989
5303
|
0u,
|
|
4990
|
-
0u
|
|
4991
5304
|
);
|
|
4992
5305
|
}
|
|
4993
5306
|
return ScatterResult(
|
|
4994
5307
|
vec4<f32>(refract_direction(ray.direction.xyz, normal, etaRatio), 0.0),
|
|
4995
5308
|
1.0,
|
|
5309
|
+
transmitted_medium_ref_id(ray, hit),
|
|
4996
5310
|
RAY_FLAG_DELTA_SAMPLE,
|
|
4997
5311
|
0u,
|
|
4998
|
-
0u
|
|
4999
5312
|
);
|
|
5000
5313
|
}
|
|
5001
5314
|
|
|
@@ -5010,9 +5323,9 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5010
5323
|
return ScatterResult(
|
|
5011
5324
|
vec4<f32>(guidedDirection, 0.0),
|
|
5012
5325
|
guidedPdf,
|
|
5326
|
+
ray.mediumRefId,
|
|
5013
5327
|
RAY_FLAG_GUIDED_EMISSIVE,
|
|
5014
5328
|
0u,
|
|
5015
|
-
0u
|
|
5016
5329
|
);
|
|
5017
5330
|
}
|
|
5018
5331
|
}
|
|
@@ -5020,7 +5333,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5020
5333
|
let guidedDirection = sample_environment_portal_direction(hit, seed + 131u, normal);
|
|
5021
5334
|
if (dot(normal, guidedDirection) > 0.000001) {
|
|
5022
5335
|
let guidedPdf = max(evaluate_surface_bsdf_pdf(hit, viewDirection, guidedDirection), 0.000001);
|
|
5023
|
-
return ScatterResult(vec4<f32>(guidedDirection, 0.0), guidedPdf,
|
|
5336
|
+
return ScatterResult(vec4<f32>(guidedDirection, 0.0), guidedPdf, ray.mediumRefId, 0u, 0u);
|
|
5024
5337
|
}
|
|
5025
5338
|
}
|
|
5026
5339
|
|
|
@@ -5054,7 +5367,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5054
5367
|
);
|
|
5055
5368
|
}
|
|
5056
5369
|
let pdf = max(evaluate_surface_bsdf_pdf(hit, viewDirection, lightDirection), 0.000001);
|
|
5057
|
-
return ScatterResult(vec4<f32>(lightDirection, 0.0), pdf,
|
|
5370
|
+
return ScatterResult(vec4<f32>(lightDirection, 0.0), pdf, ray.mediumRefId, 0u, 0u);
|
|
5058
5371
|
}
|
|
5059
5372
|
|
|
5060
5373
|
@compute @workgroup_size(64)
|
|
@@ -5067,15 +5380,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5067
5380
|
|
|
5068
5381
|
let ray = activeQueue[index];
|
|
5069
5382
|
let hit = hits[index];
|
|
5383
|
+
let segmentTransmittance = medium_transmittance(ray.mediumRefId, hit.distance);
|
|
5384
|
+
let arrivingThroughput = ray.throughput.xyz * segmentTransmittance;
|
|
5070
5385
|
var contribution = vec3<f32>(0.0);
|
|
5071
5386
|
|
|
5072
5387
|
if (hit.hitType == 1u) {
|
|
5073
5388
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
5074
5389
|
let sourceRadiance = max(hit.emission.xyz, hit.color.xyz) * guidedLightWeight;
|
|
5075
5390
|
if (deferred_path_resolve_enabled()) {
|
|
5076
|
-
record_deferred_terminal_source(ray, sourceRadiance);
|
|
5391
|
+
record_deferred_terminal_source(ray, sourceRadiance * segmentTransmittance);
|
|
5077
5392
|
} else {
|
|
5078
|
-
contribution = clamp_sample_radiance(
|
|
5393
|
+
contribution = clamp_sample_radiance(arrivingThroughput * sourceRadiance);
|
|
5079
5394
|
accumulation[ray.rayId] =
|
|
5080
5395
|
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
5081
5396
|
}
|
|
@@ -5092,9 +5407,9 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5092
5407
|
sourceRadiance = sourceRadiance * misWeight;
|
|
5093
5408
|
}
|
|
5094
5409
|
if (deferred_path_resolve_enabled()) {
|
|
5095
|
-
record_deferred_terminal_source(ray, sourceRadiance);
|
|
5410
|
+
record_deferred_terminal_source(ray, sourceRadiance * segmentTransmittance);
|
|
5096
5411
|
} else {
|
|
5097
|
-
contribution = clamp_sample_radiance(
|
|
5412
|
+
contribution = clamp_sample_radiance(arrivingThroughput * sourceRadiance);
|
|
5098
5413
|
accumulation[ray.rayId] =
|
|
5099
5414
|
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
5100
5415
|
}
|
|
@@ -5102,7 +5417,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5102
5417
|
return;
|
|
5103
5418
|
}
|
|
5104
5419
|
|
|
5105
|
-
let response = stabilize_surface_path_response(
|
|
5420
|
+
let response = stabilize_surface_path_response(
|
|
5421
|
+
ray,
|
|
5422
|
+
hit,
|
|
5423
|
+
surface_path_response(hit) * segmentTransmittance
|
|
5424
|
+
);
|
|
5106
5425
|
record_deferred_path_response(ray, response);
|
|
5107
5426
|
|
|
5108
5427
|
let shouldEstimateDirectEnvironment =
|
|
@@ -5110,7 +5429,22 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5110
5429
|
hit.material.z >= 0.95 &&
|
|
5111
5430
|
ray.bounce < 2u;
|
|
5112
5431
|
if (shouldEstimateDirectEnvironment) {
|
|
5113
|
-
let directEnvironment = surface_direct_environment_contribution(
|
|
5432
|
+
let directEnvironment = surface_direct_environment_contribution(
|
|
5433
|
+
RayRecord(
|
|
5434
|
+
ray.rayId,
|
|
5435
|
+
ray.parentRayId,
|
|
5436
|
+
ray.sourcePixelId,
|
|
5437
|
+
ray.sampleId,
|
|
5438
|
+
ray.bounce,
|
|
5439
|
+
ray.mediumRefId,
|
|
5440
|
+
ray.flags,
|
|
5441
|
+
0u,
|
|
5442
|
+
ray.origin,
|
|
5443
|
+
ray.direction,
|
|
5444
|
+
vec4<f32>(arrivingThroughput, ray.throughput.w)
|
|
5445
|
+
),
|
|
5446
|
+
hit
|
|
5447
|
+
);
|
|
5114
5448
|
accumulation[ray.rayId] =
|
|
5115
5449
|
accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
|
|
5116
5450
|
}
|
|
@@ -5119,7 +5453,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5119
5453
|
if (deferred_path_resolve_enabled()) {
|
|
5120
5454
|
record_deferred_terminal_source(ray, terminal_surface_environment_source(ray, hit));
|
|
5121
5455
|
} else {
|
|
5122
|
-
let terminalEnvironment = terminal_surface_environment_contribution(
|
|
5456
|
+
let terminalEnvironment = terminal_surface_environment_contribution(
|
|
5457
|
+
ray,
|
|
5458
|
+
arrivingThroughput,
|
|
5459
|
+
hit
|
|
5460
|
+
);
|
|
5123
5461
|
accumulation[ray.rayId] =
|
|
5124
5462
|
accumulation[ray.rayId] + vec4<f32>(terminalEnvironment * sample_weight(), 1.0);
|
|
5125
5463
|
}
|
|
@@ -5134,7 +5472,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5134
5472
|
if (deferred_path_resolve_enabled()) {
|
|
5135
5473
|
record_deferred_terminal_source(ray, terminal_surface_environment_source(ray, hit));
|
|
5136
5474
|
} else {
|
|
5137
|
-
let overflowEnvironment = terminal_surface_environment_contribution(
|
|
5475
|
+
let overflowEnvironment = terminal_surface_environment_contribution(
|
|
5476
|
+
ray,
|
|
5477
|
+
arrivingThroughput,
|
|
5478
|
+
hit
|
|
5479
|
+
);
|
|
5138
5480
|
accumulation[ray.rayId] =
|
|
5139
5481
|
accumulation[ray.rayId] + vec4<f32>(overflowEnvironment * sample_weight(), 1.0);
|
|
5140
5482
|
}
|
|
@@ -5148,7 +5490,7 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5148
5490
|
ray.sourcePixelId,
|
|
5149
5491
|
ray.sampleId,
|
|
5150
5492
|
ray.bounce + 1u,
|
|
5151
|
-
|
|
5493
|
+
scatter.mediumRefId,
|
|
5152
5494
|
scatter.flags,
|
|
5153
5495
|
0u,
|
|
5154
5496
|
vec4<f32>(offset_origin(hit.position.xyz, hit.shadingNormal.xyz), 1.0),
|
|
@@ -5637,6 +5979,11 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5637
5979
|
config.environmentMap,
|
|
5638
5980
|
config.environmentColor
|
|
5639
5981
|
);
|
|
5982
|
+
let mediumTextureResource = createMediumTextureResource(
|
|
5983
|
+
device,
|
|
5984
|
+
constants,
|
|
5985
|
+
config.mediums
|
|
5986
|
+
);
|
|
5640
5987
|
config = Object.freeze({
|
|
5641
5988
|
...config,
|
|
5642
5989
|
environmentMap: Object.freeze({
|
|
@@ -5723,7 +6070,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5723
6070
|
{ binding: 28, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
5724
6071
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
5725
6072
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
5726
|
-
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
6073
|
+
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
6074
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
5727
6075
|
]
|
|
5728
6076
|
});
|
|
5729
6077
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -5910,14 +6258,18 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5910
6258
|
{ binding: 28, resource: materialAtlasSampler },
|
|
5911
6259
|
{ binding: 29, resource: brdfLutResource.view },
|
|
5912
6260
|
{ binding: 30, resource: brdfLutResource.sampler },
|
|
5913
|
-
{ binding: 31, resource: environmentSamplingResource.view }
|
|
6261
|
+
{ binding: 31, resource: environmentSamplingResource.view },
|
|
6262
|
+
{ binding: 32, resource: mediumTextureResource.view }
|
|
5914
6263
|
]
|
|
5915
6264
|
});
|
|
5916
6265
|
}
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
6266
|
+
function createTraceBindGroups() {
|
|
6267
|
+
return [
|
|
6268
|
+
createTraceBindGroup(activeQueue, nextQueue, "plasius.wavefront.bind.activeNext"),
|
|
6269
|
+
createTraceBindGroup(nextQueue, activeQueue, "plasius.wavefront.bind.nextActive")
|
|
6270
|
+
];
|
|
6271
|
+
}
|
|
6272
|
+
let bindGroups = createTraceBindGroups();
|
|
5921
6273
|
const bvhBuildBindGroup = device.createBindGroup({
|
|
5922
6274
|
label: "plasius.wavefront.bind.bvhBuild",
|
|
5923
6275
|
layout: accelerationBindGroupLayout,
|
|
@@ -6095,6 +6447,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6095
6447
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
6096
6448
|
environmentPortalCount: config.environmentPortalCount,
|
|
6097
6449
|
environmentPortalMode: config.environmentPortalMode,
|
|
6450
|
+
mediumCount: config.mediumCount,
|
|
6098
6451
|
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
6099
6452
|
deferredPathResolve: config.deferredPathResolve,
|
|
6100
6453
|
bvhNodeCount: config.bvhNodeCount,
|
|
@@ -6642,10 +6995,22 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6642
6995
|
...overrides
|
|
6643
6996
|
});
|
|
6644
6997
|
}
|
|
6998
|
+
function rebuildMediumResources(nextConfig) {
|
|
6999
|
+
const previousMediumTextureResource = mediumTextureResource;
|
|
7000
|
+
mediumTextureResource = createMediumTextureResource(device, constants, nextConfig.mediums);
|
|
7001
|
+
bindGroups = createTraceBindGroups();
|
|
7002
|
+
if (previousMediumTextureResource?.ownsTexture) {
|
|
7003
|
+
previousMediumTextureResource.texture?.destroy?.();
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
6645
7006
|
function updateSceneObjects(sceneObjects) {
|
|
6646
7007
|
const nextPackedScene = packWavefrontSceneObjects(sceneObjects, config.sceneObjectCapacity);
|
|
6647
7008
|
packedScene = nextPackedScene;
|
|
6648
|
-
|
|
7009
|
+
const nextConfig = rebuildLiveConfig();
|
|
7010
|
+
if (!mediumTablesEqual(config.mediums, nextConfig.mediums)) {
|
|
7011
|
+
rebuildMediumResources(nextConfig);
|
|
7012
|
+
}
|
|
7013
|
+
config = nextConfig;
|
|
6649
7014
|
device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
|
|
6650
7015
|
return config;
|
|
6651
7016
|
}
|
|
@@ -6669,6 +7034,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6669
7034
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
6670
7035
|
environmentPortalCount: config.environmentPortalCount,
|
|
6671
7036
|
environmentPortalMode: config.environmentPortalMode,
|
|
7037
|
+
mediumCount: config.mediumCount,
|
|
6672
7038
|
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
6673
7039
|
deferredPathResolve: config.deferredPathResolve,
|
|
6674
7040
|
bvhNodeCount: config.bvhNodeCount,
|
|
@@ -6709,6 +7075,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6709
7075
|
if (environmentSamplingResource.ownsTexture) {
|
|
6710
7076
|
environmentSamplingResource.texture?.destroy?.();
|
|
6711
7077
|
}
|
|
7078
|
+
if (mediumTextureResource.ownsTexture) {
|
|
7079
|
+
mediumTextureResource.texture?.destroy?.();
|
|
7080
|
+
}
|
|
6712
7081
|
brdfLutResource.texture?.destroy?.();
|
|
6713
7082
|
if (baseColorAtlasResource.ownsTexture) {
|
|
6714
7083
|
baseColorAtlasResource.texture?.destroy?.();
|