@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.cjs
CHANGED
|
@@ -212,17 +212,19 @@ var DEFAULT_MAX_DEPTH = 6;
|
|
|
212
212
|
var DEFAULT_TILE_SIZE = 128;
|
|
213
213
|
var DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
214
214
|
var MAX_SAMPLES_PER_PIXEL = 256;
|
|
215
|
-
var DEFAULT_BRDF_LUT_SIZE =
|
|
215
|
+
var DEFAULT_BRDF_LUT_SIZE = 128;
|
|
216
|
+
var DEFAULT_BRDF_LUT_SAMPLE_COUNT = 256;
|
|
216
217
|
var DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
|
|
217
218
|
var DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
218
219
|
var DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
220
|
+
var DEFAULT_MEDIUM_PHASE_MODEL = 0;
|
|
219
221
|
var WORKGROUP_SIZE = 64;
|
|
220
222
|
var rendererWavefrontComputeMode = "webgpu-compute";
|
|
221
223
|
var rendererWavefrontComputeWorkgroupSize = WORKGROUP_SIZE;
|
|
222
224
|
var rendererWavefrontComputeStatsStride = 8;
|
|
223
225
|
var RAY_RECORD_BYTES = 80;
|
|
224
226
|
var HIT_RECORD_BYTES = 256;
|
|
225
|
-
var SCENE_OBJECT_RECORD_BYTES =
|
|
227
|
+
var SCENE_OBJECT_RECORD_BYTES = 160;
|
|
226
228
|
var MESH_VERTEX_RECORD_BYTES = 48;
|
|
227
229
|
var MESH_RANGE_RECORD_BYTES = 240;
|
|
228
230
|
var TRIANGLE_RECORD_BYTES = 352;
|
|
@@ -231,6 +233,7 @@ var BVH_NODE_RECORD_BYTES = 48;
|
|
|
231
233
|
var BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
232
234
|
var EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
233
235
|
var ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
236
|
+
var MEDIUM_TABLE_ROWS = 2;
|
|
234
237
|
var ACCUMULATION_RECORD_BYTES = 16;
|
|
235
238
|
var PATH_VERTEX_RECORD_BYTES = 16;
|
|
236
239
|
var GPU_SUBMITTED_WORK_TIMEOUT_MS = 5e3;
|
|
@@ -577,6 +580,156 @@ function deriveBounds(input) {
|
|
|
577
580
|
}
|
|
578
581
|
return null;
|
|
579
582
|
}
|
|
583
|
+
function deriveBeerLambertAbsorptionFromAttenuationColor(attenuationColor, attenuationDistance, density = 1) {
|
|
584
|
+
const distance = Number(attenuationDistance);
|
|
585
|
+
const densityScale = Math.max(0, Number(density) || 0);
|
|
586
|
+
if (!Number.isFinite(distance) || distance <= 0 || densityScale <= 0) {
|
|
587
|
+
return [0, 0, 0];
|
|
588
|
+
}
|
|
589
|
+
return attenuationColor.slice(0, 3).map((channel) => {
|
|
590
|
+
const clamped = clamp(Number(channel) || 0, 1e-4, 1);
|
|
591
|
+
return Math.max(0, -Math.log(clamped) / distance * densityScale);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function readMediumPhaseModel(value) {
|
|
595
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
596
|
+
return Math.max(0, Math.trunc(value));
|
|
597
|
+
}
|
|
598
|
+
switch (String(value ?? "").trim().toLowerCase()) {
|
|
599
|
+
case "isotropic":
|
|
600
|
+
default:
|
|
601
|
+
return DEFAULT_MEDIUM_PHASE_MODEL;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function resolveWavefrontVolumeInput(input) {
|
|
605
|
+
return input?.volume ?? input?.material?.volume ?? null;
|
|
606
|
+
}
|
|
607
|
+
function normalizeWavefrontThickness(input, label) {
|
|
608
|
+
const volume = resolveWavefrontVolumeInput(input);
|
|
609
|
+
return Math.max(
|
|
610
|
+
0,
|
|
611
|
+
readFiniteNumber(
|
|
612
|
+
label,
|
|
613
|
+
input?.thickness ?? volume?.thickness ?? input?.material?.thickness,
|
|
614
|
+
0
|
|
615
|
+
)
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
function resolveWavefrontMediumId(input, fallbackId = 1) {
|
|
619
|
+
return input?.mediumRefId ?? input?.mediumId ?? input?.material?.mediumId ?? input?.materialRefId ?? input?.material?.id ?? input?.materialId ?? input?.id ?? fallbackId;
|
|
620
|
+
}
|
|
621
|
+
function deriveWavefrontTransportMedium(input, fallbackId = 1) {
|
|
622
|
+
const resolvedId = resolveWavefrontMediumId(input, fallbackId);
|
|
623
|
+
if (input?.medium) {
|
|
624
|
+
return normalizeWavefrontMedium(
|
|
625
|
+
{
|
|
626
|
+
...input.medium,
|
|
627
|
+
id: input.medium.id ?? input.medium.mediumId ?? resolvedId
|
|
628
|
+
},
|
|
629
|
+
fallbackId
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const volume = resolveWavefrontVolumeInput(input);
|
|
633
|
+
if (!volume) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
return normalizeWavefrontMedium(
|
|
637
|
+
{
|
|
638
|
+
id: resolvedId,
|
|
639
|
+
phaseModel: volume.phaseModel,
|
|
640
|
+
density: volume.density,
|
|
641
|
+
attenuationColor: volume.attenuationColor,
|
|
642
|
+
attenuationDistance: volume.attenuationDistance,
|
|
643
|
+
absorption: volume.absorption,
|
|
644
|
+
scattering: volume.scattering
|
|
645
|
+
},
|
|
646
|
+
fallbackId
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
function normalizeWavefrontMedium(input = {}, index = 0) {
|
|
650
|
+
const id = readNonNegativeInteger("medium id", input.id ?? input.mediumId, index);
|
|
651
|
+
const density = Math.max(0, readFiniteNumber("medium density", input.density, 1));
|
|
652
|
+
const attenuationColor = asColor(
|
|
653
|
+
input.attenuationColor ?? input.color ?? input.medium?.attenuationColor,
|
|
654
|
+
[1, 1, 1, 1]
|
|
655
|
+
);
|
|
656
|
+
const attenuationDistance = readFiniteNumber(
|
|
657
|
+
"medium attenuationDistance",
|
|
658
|
+
input.attenuationDistance ?? input.distance ?? input.medium?.attenuationDistance,
|
|
659
|
+
0
|
|
660
|
+
);
|
|
661
|
+
const absorption = Array.isArray(input.absorption) || Array.isArray(input.medium?.absorption) ? asVec3(input.absorption ?? input.medium?.absorption, [0, 0, 0]).map(
|
|
662
|
+
(value) => Math.max(0, Number(value) || 0)
|
|
663
|
+
) : deriveBeerLambertAbsorptionFromAttenuationColor(
|
|
664
|
+
attenuationColor,
|
|
665
|
+
attenuationDistance,
|
|
666
|
+
density
|
|
667
|
+
);
|
|
668
|
+
const scattering = asVec3(
|
|
669
|
+
input.scattering ?? input.medium?.scattering,
|
|
670
|
+
[0, 0, 0]
|
|
671
|
+
).map((value) => Math.max(0, Number(value) || 0));
|
|
672
|
+
return Object.freeze({
|
|
673
|
+
id,
|
|
674
|
+
phaseModel: readMediumPhaseModel(input.phaseModel ?? input.medium?.phaseModel),
|
|
675
|
+
density,
|
|
676
|
+
attenuationColor: Object.freeze(attenuationColor),
|
|
677
|
+
attenuationDistance,
|
|
678
|
+
absorption: Object.freeze(absorption),
|
|
679
|
+
scattering: Object.freeze(scattering)
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
function collectWavefrontMediums(options, meshes, sceneObjects = []) {
|
|
683
|
+
const mediumsById = /* @__PURE__ */ new Map();
|
|
684
|
+
mediumsById.set(
|
|
685
|
+
0,
|
|
686
|
+
Object.freeze({
|
|
687
|
+
id: 0,
|
|
688
|
+
phaseModel: DEFAULT_MEDIUM_PHASE_MODEL,
|
|
689
|
+
density: 0,
|
|
690
|
+
attenuationColor: Object.freeze([1, 1, 1, 1]),
|
|
691
|
+
attenuationDistance: 0,
|
|
692
|
+
absorption: Object.freeze([0, 0, 0]),
|
|
693
|
+
scattering: Object.freeze([0, 0, 0])
|
|
694
|
+
})
|
|
695
|
+
);
|
|
696
|
+
const register = (input, fallbackId = mediumsById.size) => {
|
|
697
|
+
if (!input) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const normalized = normalizeWavefrontMedium(
|
|
701
|
+
typeof input === "object" ? { id: fallbackId, ...input } : { id: fallbackId },
|
|
702
|
+
fallbackId
|
|
703
|
+
);
|
|
704
|
+
const existing = mediumsById.get(normalized.id);
|
|
705
|
+
if (existing && JSON.stringify(existing) !== JSON.stringify(normalized)) {
|
|
706
|
+
throw new Error(`Medium id ${normalized.id} is defined more than once with different values.`);
|
|
707
|
+
}
|
|
708
|
+
mediumsById.set(normalized.id, normalized);
|
|
709
|
+
};
|
|
710
|
+
for (const medium of options.mediums ?? []) {
|
|
711
|
+
register(medium);
|
|
712
|
+
}
|
|
713
|
+
for (const mesh of meshes) {
|
|
714
|
+
register(mesh.medium, mesh.mediumRefId ?? mesh.medium?.id ?? 0);
|
|
715
|
+
}
|
|
716
|
+
for (const mesh of meshes) {
|
|
717
|
+
if ((mesh.mediumRefId ?? 0) > 0 && !mediumsById.has(mesh.mediumRefId)) {
|
|
718
|
+
register({ id: mesh.mediumRefId });
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
for (const object of sceneObjects) {
|
|
722
|
+
register(object.medium, object.mediumRefId ?? object.medium?.id ?? 0);
|
|
723
|
+
}
|
|
724
|
+
for (const object of sceneObjects) {
|
|
725
|
+
if ((object.mediumRefId ?? 0) > 0 && !mediumsById.has(object.mediumRefId)) {
|
|
726
|
+
register({ id: object.mediumRefId });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return Object.freeze(
|
|
730
|
+
Array.from(mediumsById.values()).sort((left, right) => left.id - right.id)
|
|
731
|
+
);
|
|
732
|
+
}
|
|
580
733
|
function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
581
734
|
const bounds = deriveBounds(input);
|
|
582
735
|
const kind = readObjectKind(input.kind ?? input.type ?? (bounds ? "box" : "sphere"));
|
|
@@ -607,12 +760,19 @@ function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
|
607
760
|
input.specularColor ?? input.material?.specularColor,
|
|
608
761
|
[1, 1, 1, 1]
|
|
609
762
|
).map((value, componentIndex) => componentIndex < 3 ? clamp(value, 0, 1) : 1);
|
|
763
|
+
const medium = deriveWavefrontTransportMedium(input, index + 1);
|
|
610
764
|
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;
|
|
611
765
|
return Object.freeze({
|
|
612
766
|
id: readNonNegativeInteger("id", input.id, index + 1),
|
|
613
767
|
kind,
|
|
614
768
|
materialKind: resolvedMaterialKind,
|
|
615
769
|
flags: readNonNegativeInteger("flags", input.flags, 0),
|
|
770
|
+
mediumRefId: readNonNegativeInteger(
|
|
771
|
+
"mediumRefId",
|
|
772
|
+
input.mediumRefId ?? medium?.id ?? input.medium?.id ?? input.mediumId,
|
|
773
|
+
0
|
|
774
|
+
),
|
|
775
|
+
medium,
|
|
616
776
|
center: Object.freeze(center),
|
|
617
777
|
halfExtent: Object.freeze(halfExtent),
|
|
618
778
|
color: Object.freeze(color),
|
|
@@ -636,6 +796,7 @@ function normalizeWavefrontSceneObject(input = {}, index = 0) {
|
|
|
636
796
|
),
|
|
637
797
|
specular: clamp(readFiniteNumber("specular", input.specular ?? input.material?.specular, 1), 0, 1),
|
|
638
798
|
specularColor: Object.freeze(specularColor),
|
|
799
|
+
thickness: normalizeWavefrontThickness(input, "thickness"),
|
|
639
800
|
transmission
|
|
640
801
|
});
|
|
641
802
|
}
|
|
@@ -729,6 +890,7 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
729
890
|
input.specularColor ?? input.material?.specularColor,
|
|
730
891
|
[1, 1, 1, 1]
|
|
731
892
|
).map((value, componentIndex) => componentIndex < 3 ? clamp(value, 0, 1) : 1);
|
|
893
|
+
const medium = deriveWavefrontTransportMedium(input, meshIndex + 1);
|
|
732
894
|
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;
|
|
733
895
|
return Object.freeze({
|
|
734
896
|
id: readNonNegativeInteger("mesh id", input.id, meshIndex + 1),
|
|
@@ -745,9 +907,10 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
745
907
|
),
|
|
746
908
|
mediumRefId: readNonNegativeInteger(
|
|
747
909
|
"mesh mediumRefId",
|
|
748
|
-
input.mediumRefId ?? input.medium?.id ?? input.mediumId,
|
|
910
|
+
input.mediumRefId ?? medium?.id ?? input.medium?.id ?? input.mediumId ?? input.material?.mediumId,
|
|
749
911
|
0
|
|
750
912
|
),
|
|
913
|
+
medium,
|
|
751
914
|
color: Object.freeze(color),
|
|
752
915
|
emission: Object.freeze(emission),
|
|
753
916
|
roughness: clamp(readFiniteNumber("roughness", input.roughness ?? input.material?.roughness, 0.72), 0, 1),
|
|
@@ -769,6 +932,7 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
|
|
|
769
932
|
),
|
|
770
933
|
specular: clamp(readFiniteNumber("specular", input.specular ?? input.material?.specular, 1), 0, 1),
|
|
771
934
|
specularColor: Object.freeze(specularColor),
|
|
935
|
+
thickness: normalizeWavefrontThickness(input, "mesh thickness"),
|
|
772
936
|
transmission,
|
|
773
937
|
baseColorTexture: input.baseColorTexture ?? input.material?.baseColorTexture ?? null,
|
|
774
938
|
metallicRoughnessTexture: input.metallicRoughnessTexture ?? input.material?.metallicRoughnessTexture ?? null,
|
|
@@ -980,7 +1144,7 @@ function createMeshTriangleRecords(meshes) {
|
|
|
980
1144
|
mesh.clearcoatRoughness,
|
|
981
1145
|
mesh.specular,
|
|
982
1146
|
mesh.transmission,
|
|
983
|
-
|
|
1147
|
+
mesh.thickness
|
|
984
1148
|
]),
|
|
985
1149
|
specularColor: Object.freeze([
|
|
986
1150
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1274,7 +1438,7 @@ function createWavefrontGpuMaterialSource(meshes = []) {
|
|
|
1274
1438
|
mesh.clearcoatRoughness,
|
|
1275
1439
|
mesh.specular,
|
|
1276
1440
|
mesh.transmission,
|
|
1277
|
-
|
|
1441
|
+
mesh.thickness
|
|
1278
1442
|
]);
|
|
1279
1443
|
writeVec4(floatView, byteOffset + 80, [
|
|
1280
1444
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1442,7 +1606,7 @@ function createWavefrontGpuMeshSource(meshes = [], gpuMaterialSourceInput = null
|
|
|
1442
1606
|
mesh.clearcoatRoughness,
|
|
1443
1607
|
mesh.specular,
|
|
1444
1608
|
mesh.transmission,
|
|
1445
|
-
|
|
1609
|
+
mesh.thickness
|
|
1446
1610
|
]);
|
|
1447
1611
|
writeVec4(meshFloats, floatOffset * 4 + 128, [
|
|
1448
1612
|
mesh.specularColor[0] ?? 1,
|
|
@@ -1543,12 +1707,16 @@ function normalizeSceneObjects(sceneObjects, useDefaultScene = true) {
|
|
|
1543
1707
|
const source = Array.isArray(sceneObjects) && sceneObjects.length > 0 ? sceneObjects : useDefaultScene ? createDefaultWavefrontSceneObjects() : [];
|
|
1544
1708
|
return source.map((object, index) => normalizeWavefrontSceneObject(object, index));
|
|
1545
1709
|
}
|
|
1710
|
+
function normalizeWavefrontMeshes(meshes) {
|
|
1711
|
+
const source = Array.isArray(meshes) ? meshes : [];
|
|
1712
|
+
return source.map((mesh, index) => normalizeWavefrontMesh(mesh, index));
|
|
1713
|
+
}
|
|
1546
1714
|
function normalizeMeshes(options = {}) {
|
|
1547
1715
|
if (Array.isArray(options.meshes)) {
|
|
1548
|
-
return options.meshes;
|
|
1716
|
+
return normalizeWavefrontMeshes(options.meshes);
|
|
1549
1717
|
}
|
|
1550
1718
|
if (options.mesh) {
|
|
1551
|
-
return [options.mesh];
|
|
1719
|
+
return normalizeWavefrontMeshes([options.mesh]);
|
|
1552
1720
|
}
|
|
1553
1721
|
return [];
|
|
1554
1722
|
}
|
|
@@ -1855,6 +2023,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1855
2023
|
const sceneObjects = Object.freeze(
|
|
1856
2024
|
normalizeSceneObjects(options.sceneObjects, meshes.length === 0)
|
|
1857
2025
|
);
|
|
2026
|
+
const mediums = collectWavefrontMediums(options, meshes, sceneObjects);
|
|
1858
2027
|
const sceneObjectCapacity = Math.max(
|
|
1859
2028
|
sceneObjects.length,
|
|
1860
2029
|
readPositiveInteger("sceneObjectCapacity", options.sceneObjectCapacity, DEFAULT_SCENE_OBJECT_CAPACITY)
|
|
@@ -1913,6 +2082,8 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1913
2082
|
sceneObjects,
|
|
1914
2083
|
sceneObjectCount: sceneObjects.length,
|
|
1915
2084
|
sceneObjectCapacity,
|
|
2085
|
+
mediums,
|
|
2086
|
+
mediumCount: mediums.length,
|
|
1916
2087
|
accelerationBuildMode,
|
|
1917
2088
|
gpuAccelerationBuildRequired: accelerationBuildMode === "gpu" && triangleCount > 0,
|
|
1918
2089
|
gpuMeshSource,
|
|
@@ -2014,29 +2185,30 @@ function packWavefrontSceneObjects(sceneObjects, capacity = sceneObjects.length)
|
|
|
2014
2185
|
uintView[u32 + 1] = object.id;
|
|
2015
2186
|
uintView[u32 + 2] = object.materialKind;
|
|
2016
2187
|
uintView[u32 + 3] = object.flags;
|
|
2017
|
-
|
|
2018
|
-
writeVec4(floatView, byteOffset + 32, [...object.
|
|
2019
|
-
writeVec4(floatView, byteOffset + 48, object.
|
|
2020
|
-
writeVec4(floatView, byteOffset + 64, object.
|
|
2021
|
-
writeVec4(floatView, byteOffset + 80,
|
|
2188
|
+
uintView[u32 + 4] = object.mediumRefId;
|
|
2189
|
+
writeVec4(floatView, byteOffset + 32, [...object.center, 0]);
|
|
2190
|
+
writeVec4(floatView, byteOffset + 48, [...object.halfExtent, 0]);
|
|
2191
|
+
writeVec4(floatView, byteOffset + 64, object.color);
|
|
2192
|
+
writeVec4(floatView, byteOffset + 80, object.emission);
|
|
2193
|
+
writeVec4(floatView, byteOffset + 96, [
|
|
2022
2194
|
object.roughness,
|
|
2023
2195
|
object.metallic,
|
|
2024
2196
|
object.opacity,
|
|
2025
2197
|
object.ior
|
|
2026
2198
|
]);
|
|
2027
|
-
writeVec4(floatView, byteOffset +
|
|
2199
|
+
writeVec4(floatView, byteOffset + 112, [
|
|
2028
2200
|
object.sheenColor[0] ?? 0,
|
|
2029
2201
|
object.sheenColor[1] ?? 0,
|
|
2030
2202
|
object.sheenColor[2] ?? 0,
|
|
2031
2203
|
object.clearcoat
|
|
2032
2204
|
]);
|
|
2033
|
-
writeVec4(floatView, byteOffset +
|
|
2205
|
+
writeVec4(floatView, byteOffset + 128, [
|
|
2034
2206
|
object.clearcoatRoughness,
|
|
2035
2207
|
object.specular,
|
|
2036
2208
|
object.transmission,
|
|
2037
|
-
|
|
2209
|
+
object.thickness
|
|
2038
2210
|
]);
|
|
2039
|
-
writeVec4(floatView, byteOffset +
|
|
2211
|
+
writeVec4(floatView, byteOffset + 144, [
|
|
2040
2212
|
object.specularColor[0] ?? 1,
|
|
2041
2213
|
object.specularColor[1] ?? 1,
|
|
2042
2214
|
object.specularColor[2] ?? 1,
|
|
@@ -2572,7 +2744,7 @@ function integrateBrdfSample(nDotV, roughness, sampleCount) {
|
|
|
2572
2744
|
}
|
|
2573
2745
|
return [scaleTerm / sampleCount, biasTerm / sampleCount];
|
|
2574
2746
|
}
|
|
2575
|
-
function createBrdfLutUploadBytes(size = DEFAULT_BRDF_LUT_SIZE, sampleCount =
|
|
2747
|
+
function createBrdfLutUploadBytes(size = DEFAULT_BRDF_LUT_SIZE, sampleCount = DEFAULT_BRDF_LUT_SAMPLE_COUNT) {
|
|
2576
2748
|
const cacheKey = `${Math.max(1, Math.trunc(size))}:${Math.max(1, Math.trunc(sampleCount))}`;
|
|
2577
2749
|
const cached = BRDF_LUT_UPLOAD_CACHE.get(cacheKey);
|
|
2578
2750
|
if (cached) {
|
|
@@ -2944,6 +3116,80 @@ function createBrdfLutResource(device, constants, size = DEFAULT_BRDF_LUT_SIZE)
|
|
|
2944
3116
|
height: upload.height
|
|
2945
3117
|
});
|
|
2946
3118
|
}
|
|
3119
|
+
function createMediumTextureResource(device, constants, mediums) {
|
|
3120
|
+
const normalized = Array.isArray(mediums) && mediums.length > 0 ? mediums : [{ id: 0 }];
|
|
3121
|
+
const width = Math.max(
|
|
3122
|
+
1,
|
|
3123
|
+
normalized.reduce((maximum, medium) => Math.max(maximum, medium.id ?? 0), 0) + 1
|
|
3124
|
+
);
|
|
3125
|
+
const level = {
|
|
3126
|
+
width,
|
|
3127
|
+
height: MEDIUM_TABLE_ROWS,
|
|
3128
|
+
data: new Float32Array(width * MEDIUM_TABLE_ROWS * 4)
|
|
3129
|
+
};
|
|
3130
|
+
for (const medium of normalized) {
|
|
3131
|
+
const mediumId = Math.max(0, Math.trunc(Number(medium.id) || 0));
|
|
3132
|
+
const absorptionOffset = mediumId * 4;
|
|
3133
|
+
level.data[absorptionOffset] = Math.max(0, medium.absorption?.[0] ?? 0);
|
|
3134
|
+
level.data[absorptionOffset + 1] = Math.max(0, medium.absorption?.[1] ?? 0);
|
|
3135
|
+
level.data[absorptionOffset + 2] = Math.max(0, medium.absorption?.[2] ?? 0);
|
|
3136
|
+
level.data[absorptionOffset + 3] = Math.max(0, medium.phaseModel ?? 0);
|
|
3137
|
+
const scatteringOffset = (width + mediumId) * 4;
|
|
3138
|
+
level.data[scatteringOffset] = Math.max(0, medium.scattering?.[0] ?? 0);
|
|
3139
|
+
level.data[scatteringOffset + 1] = Math.max(0, medium.scattering?.[1] ?? 0);
|
|
3140
|
+
level.data[scatteringOffset + 2] = Math.max(0, medium.scattering?.[2] ?? 0);
|
|
3141
|
+
level.data[scatteringOffset + 3] = Math.max(0, medium.density ?? 0);
|
|
3142
|
+
}
|
|
3143
|
+
const upload = createFloat16RgbaUploadFromLevels([level])[0];
|
|
3144
|
+
const texture = device.createTexture({
|
|
3145
|
+
label: "plasius.wavefront.mediumTable",
|
|
3146
|
+
size: { width, height: MEDIUM_TABLE_ROWS },
|
|
3147
|
+
format: "rgba16float",
|
|
3148
|
+
usage: constants.texture.TEXTURE_BINDING | constants.texture.COPY_DST
|
|
3149
|
+
});
|
|
3150
|
+
device.queue.writeTexture(
|
|
3151
|
+
{ texture },
|
|
3152
|
+
upload.bytes,
|
|
3153
|
+
{ bytesPerRow: upload.bytesPerRow, rowsPerImage: upload.height },
|
|
3154
|
+
{ width, height: MEDIUM_TABLE_ROWS, depthOrArrayLayers: 1 }
|
|
3155
|
+
);
|
|
3156
|
+
return Object.freeze({
|
|
3157
|
+
texture,
|
|
3158
|
+
view: texture.createView(),
|
|
3159
|
+
ownsTexture: true,
|
|
3160
|
+
count: normalized.length,
|
|
3161
|
+
width
|
|
3162
|
+
});
|
|
3163
|
+
}
|
|
3164
|
+
function mediumTablesEqual(left, right) {
|
|
3165
|
+
const leftMediums = Array.isArray(left) ? left : [];
|
|
3166
|
+
const rightMediums = Array.isArray(right) ? right : [];
|
|
3167
|
+
if (leftMediums.length !== rightMediums.length) {
|
|
3168
|
+
return false;
|
|
3169
|
+
}
|
|
3170
|
+
for (let index = 0; index < leftMediums.length; index += 1) {
|
|
3171
|
+
const leftMedium = leftMediums[index];
|
|
3172
|
+
const rightMedium = rightMediums[index];
|
|
3173
|
+
if ((leftMedium?.id ?? 0) !== (rightMedium?.id ?? 0)) {
|
|
3174
|
+
return false;
|
|
3175
|
+
}
|
|
3176
|
+
if ((leftMedium?.phaseModel ?? 0) !== (rightMedium?.phaseModel ?? 0)) {
|
|
3177
|
+
return false;
|
|
3178
|
+
}
|
|
3179
|
+
if ((leftMedium?.density ?? 0) !== (rightMedium?.density ?? 0)) {
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3182
|
+
for (let component = 0; component < 3; component += 1) {
|
|
3183
|
+
if ((leftMedium?.absorption?.[component] ?? 0) !== (rightMedium?.absorption?.[component] ?? 0)) {
|
|
3184
|
+
return false;
|
|
3185
|
+
}
|
|
3186
|
+
if ((leftMedium?.scattering?.[component] ?? 0) !== (rightMedium?.scattering?.[component] ?? 0)) {
|
|
3187
|
+
return false;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return true;
|
|
3192
|
+
}
|
|
2947
3193
|
function createAtlasTextureResource(device, constants, atlas, label) {
|
|
2948
3194
|
const upload = createRgba8TextureUpload(atlas);
|
|
2949
3195
|
const texture = device.createTexture({
|
|
@@ -3082,6 +3328,10 @@ struct SceneObject {
|
|
|
3082
3328
|
objectId: u32,
|
|
3083
3329
|
materialKind: u32,
|
|
3084
3330
|
flags: u32,
|
|
3331
|
+
mediumRefId: u32,
|
|
3332
|
+
pad0: u32,
|
|
3333
|
+
pad1: u32,
|
|
3334
|
+
pad2: u32,
|
|
3085
3335
|
center: vec4<f32>,
|
|
3086
3336
|
halfExtent: vec4<f32>,
|
|
3087
3337
|
color: vec4<f32>,
|
|
@@ -3142,9 +3392,9 @@ struct BvhLeafRef {
|
|
|
3142
3392
|
struct ScatterResult {
|
|
3143
3393
|
direction: vec4<f32>,
|
|
3144
3394
|
pdf: f32,
|
|
3395
|
+
mediumRefId: u32,
|
|
3145
3396
|
flags: u32,
|
|
3146
3397
|
pad0: u32,
|
|
3147
|
-
pad1: u32,
|
|
3148
3398
|
};
|
|
3149
3399
|
|
|
3150
3400
|
struct MeshVertex {
|
|
@@ -3290,6 +3540,7 @@ struct EnvironmentPortal {
|
|
|
3290
3540
|
@group(0) @binding(29) var brdfLutTexture: texture_2d<f32>;
|
|
3291
3541
|
@group(0) @binding(30) var brdfLutSampler: sampler;
|
|
3292
3542
|
@group(0) @binding(31) var environmentSamplingTexture: texture_2d<f32>;
|
|
3543
|
+
@group(0) @binding(32) var mediumTableTexture: texture_2d<f32>;
|
|
3293
3544
|
|
|
3294
3545
|
fn hash_u32(value: u32) -> u32 {
|
|
3295
3546
|
var x = value;
|
|
@@ -3955,6 +4206,60 @@ fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f
|
|
|
3955
4206
|
return environment_radiance(origin, direction);
|
|
3956
4207
|
}
|
|
3957
4208
|
|
|
4209
|
+
fn medium_dimensions() -> vec2<u32> {
|
|
4210
|
+
return textureDimensions(mediumTableTexture);
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
fn medium_valid(mediumRefId: u32) -> bool {
|
|
4214
|
+
let dimensions = medium_dimensions();
|
|
4215
|
+
return mediumRefId > 0u && mediumRefId < dimensions.x;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
fn medium_absorption(mediumRefId: u32) -> vec3<f32> {
|
|
4219
|
+
if (!medium_valid(mediumRefId)) {
|
|
4220
|
+
return vec3<f32>(0.0);
|
|
4221
|
+
}
|
|
4222
|
+
return max(
|
|
4223
|
+
textureLoad(mediumTableTexture, vec2<i32>(i32(mediumRefId), 0), 0).xyz,
|
|
4224
|
+
vec3<f32>(0.0)
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
fn medium_scattering(mediumRefId: u32) -> vec3<f32> {
|
|
4229
|
+
if (!medium_valid(mediumRefId)) {
|
|
4230
|
+
return vec3<f32>(0.0);
|
|
4231
|
+
}
|
|
4232
|
+
return max(
|
|
4233
|
+
textureLoad(mediumTableTexture, vec2<i32>(i32(mediumRefId), 1), 0).xyz,
|
|
4234
|
+
vec3<f32>(0.0)
|
|
4235
|
+
);
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
fn medium_transmittance(mediumRefId: u32, distance: f32) -> vec3<f32> {
|
|
4239
|
+
if (!medium_valid(mediumRefId) || distance <= 0.000001) {
|
|
4240
|
+
return vec3<f32>(1.0);
|
|
4241
|
+
}
|
|
4242
|
+
let extinction = medium_absorption(mediumRefId) + medium_scattering(mediumRefId);
|
|
4243
|
+
return vec3<f32>(
|
|
4244
|
+
exp(-extinction.x * distance),
|
|
4245
|
+
exp(-extinction.y * distance),
|
|
4246
|
+
exp(-extinction.z * distance)
|
|
4247
|
+
);
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
fn transmitted_medium_ref_id(ray: RayRecord, hit: HitRecord) -> u32 {
|
|
4251
|
+
if (hit.mediumRefId == 0u) {
|
|
4252
|
+
return ray.mediumRefId;
|
|
4253
|
+
}
|
|
4254
|
+
if (hit.frontFace == 1u) {
|
|
4255
|
+
return hit.mediumRefId;
|
|
4256
|
+
}
|
|
4257
|
+
if (ray.mediumRefId == hit.mediumRefId) {
|
|
4258
|
+
return 0u;
|
|
4259
|
+
}
|
|
4260
|
+
return ray.mediumRefId;
|
|
4261
|
+
}
|
|
4262
|
+
|
|
3958
4263
|
fn surface_path_response(hit: HitRecord) -> vec3<f32> {
|
|
3959
4264
|
let color = clamp(hit.color.xyz, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
3960
4265
|
let opacity = clamp(hit.material.z, 0.0, 1.0);
|
|
@@ -4053,11 +4358,15 @@ fn terminal_surface_environment_source(ray: RayRecord, hit: HitRecord) -> vec3<f
|
|
|
4053
4358
|
return clamp_sample_radiance(environmentFloor * materialFloor);
|
|
4054
4359
|
}
|
|
4055
4360
|
|
|
4056
|
-
fn terminal_surface_environment_contribution(
|
|
4361
|
+
fn terminal_surface_environment_contribution(
|
|
4362
|
+
ray: RayRecord,
|
|
4363
|
+
throughput: vec3<f32>,
|
|
4364
|
+
hit: HitRecord
|
|
4365
|
+
) -> vec3<f32> {
|
|
4057
4366
|
let surfaceColor = max(hit.color.xyz, config.ambientColor.xyz);
|
|
4058
4367
|
let occlusion = mix(0.75, 1.0, clamp(hit.occlusion, 0.0, 1.0));
|
|
4059
4368
|
return clamp_sample_radiance(
|
|
4060
|
-
|
|
4369
|
+
throughput *
|
|
4061
4370
|
surfaceColor *
|
|
4062
4371
|
terminal_surface_environment_source(ray, hit) *
|
|
4063
4372
|
occlusion
|
|
@@ -4531,7 +4840,7 @@ fn intersect_sphere(ray: RayRecord, object: SceneObject) -> Candidate {
|
|
|
4531
4840
|
0xffffffffu,
|
|
4532
4841
|
object.objectId,
|
|
4533
4842
|
object.objectId,
|
|
4534
|
-
|
|
4843
|
+
object.mediumRefId
|
|
4535
4844
|
);
|
|
4536
4845
|
}
|
|
4537
4846
|
|
|
@@ -4583,7 +4892,7 @@ fn intersect_box(ray: RayRecord, object: SceneObject) -> Candidate {
|
|
|
4583
4892
|
0xffffffffu,
|
|
4584
4893
|
object.objectId,
|
|
4585
4894
|
object.objectId,
|
|
4586
|
-
|
|
4895
|
+
object.mediumRefId
|
|
4587
4896
|
);
|
|
4588
4897
|
}
|
|
4589
4898
|
|
|
@@ -4834,6 +5143,10 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
4834
5143
|
let ray = activeQueue[index];
|
|
4835
5144
|
var nearest = 1000000.0;
|
|
4836
5145
|
var hitObject = SceneObject(
|
|
5146
|
+
0u,
|
|
5147
|
+
0u,
|
|
5148
|
+
0u,
|
|
5149
|
+
0u,
|
|
4837
5150
|
0u,
|
|
4838
5151
|
0u,
|
|
4839
5152
|
0u,
|
|
@@ -5037,9 +5350,9 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5037
5350
|
return ScatterResult(
|
|
5038
5351
|
vec4<f32>(reflect(ray.direction.xyz, normal), 0.0),
|
|
5039
5352
|
1.0,
|
|
5353
|
+
ray.mediumRefId,
|
|
5040
5354
|
RAY_FLAG_DELTA_SAMPLE,
|
|
5041
5355
|
0u,
|
|
5042
|
-
0u
|
|
5043
5356
|
);
|
|
5044
5357
|
}
|
|
5045
5358
|
|
|
@@ -5059,17 +5372,17 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5059
5372
|
return ScatterResult(
|
|
5060
5373
|
vec4<f32>(reflect(ray.direction.xyz, normal), 0.0),
|
|
5061
5374
|
1.0,
|
|
5375
|
+
ray.mediumRefId,
|
|
5062
5376
|
RAY_FLAG_DELTA_SAMPLE,
|
|
5063
5377
|
0u,
|
|
5064
|
-
0u
|
|
5065
5378
|
);
|
|
5066
5379
|
}
|
|
5067
5380
|
return ScatterResult(
|
|
5068
5381
|
vec4<f32>(refract_direction(ray.direction.xyz, normal, etaRatio), 0.0),
|
|
5069
5382
|
1.0,
|
|
5383
|
+
transmitted_medium_ref_id(ray, hit),
|
|
5070
5384
|
RAY_FLAG_DELTA_SAMPLE,
|
|
5071
5385
|
0u,
|
|
5072
|
-
0u
|
|
5073
5386
|
);
|
|
5074
5387
|
}
|
|
5075
5388
|
|
|
@@ -5084,9 +5397,9 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5084
5397
|
return ScatterResult(
|
|
5085
5398
|
vec4<f32>(guidedDirection, 0.0),
|
|
5086
5399
|
guidedPdf,
|
|
5400
|
+
ray.mediumRefId,
|
|
5087
5401
|
RAY_FLAG_GUIDED_EMISSIVE,
|
|
5088
5402
|
0u,
|
|
5089
|
-
0u
|
|
5090
5403
|
);
|
|
5091
5404
|
}
|
|
5092
5405
|
}
|
|
@@ -5094,7 +5407,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5094
5407
|
let guidedDirection = sample_environment_portal_direction(hit, seed + 131u, normal);
|
|
5095
5408
|
if (dot(normal, guidedDirection) > 0.000001) {
|
|
5096
5409
|
let guidedPdf = max(evaluate_surface_bsdf_pdf(hit, viewDirection, guidedDirection), 0.000001);
|
|
5097
|
-
return ScatterResult(vec4<f32>(guidedDirection, 0.0), guidedPdf,
|
|
5410
|
+
return ScatterResult(vec4<f32>(guidedDirection, 0.0), guidedPdf, ray.mediumRefId, 0u, 0u);
|
|
5098
5411
|
}
|
|
5099
5412
|
}
|
|
5100
5413
|
|
|
@@ -5128,7 +5441,7 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
5128
5441
|
);
|
|
5129
5442
|
}
|
|
5130
5443
|
let pdf = max(evaluate_surface_bsdf_pdf(hit, viewDirection, lightDirection), 0.000001);
|
|
5131
|
-
return ScatterResult(vec4<f32>(lightDirection, 0.0), pdf,
|
|
5444
|
+
return ScatterResult(vec4<f32>(lightDirection, 0.0), pdf, ray.mediumRefId, 0u, 0u);
|
|
5132
5445
|
}
|
|
5133
5446
|
|
|
5134
5447
|
@compute @workgroup_size(64)
|
|
@@ -5141,15 +5454,17 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5141
5454
|
|
|
5142
5455
|
let ray = activeQueue[index];
|
|
5143
5456
|
let hit = hits[index];
|
|
5457
|
+
let segmentTransmittance = medium_transmittance(ray.mediumRefId, hit.distance);
|
|
5458
|
+
let arrivingThroughput = ray.throughput.xyz * segmentTransmittance;
|
|
5144
5459
|
var contribution = vec3<f32>(0.0);
|
|
5145
5460
|
|
|
5146
5461
|
if (hit.hitType == 1u) {
|
|
5147
5462
|
let guidedLightWeight = select(1.0, 0.24, (ray.flags & RAY_FLAG_GUIDED_EMISSIVE) != 0u);
|
|
5148
5463
|
let sourceRadiance = max(hit.emission.xyz, hit.color.xyz) * guidedLightWeight;
|
|
5149
5464
|
if (deferred_path_resolve_enabled()) {
|
|
5150
|
-
record_deferred_terminal_source(ray, sourceRadiance);
|
|
5465
|
+
record_deferred_terminal_source(ray, sourceRadiance * segmentTransmittance);
|
|
5151
5466
|
} else {
|
|
5152
|
-
contribution = clamp_sample_radiance(
|
|
5467
|
+
contribution = clamp_sample_radiance(arrivingThroughput * sourceRadiance);
|
|
5153
5468
|
accumulation[ray.rayId] =
|
|
5154
5469
|
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
5155
5470
|
}
|
|
@@ -5166,9 +5481,9 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5166
5481
|
sourceRadiance = sourceRadiance * misWeight;
|
|
5167
5482
|
}
|
|
5168
5483
|
if (deferred_path_resolve_enabled()) {
|
|
5169
|
-
record_deferred_terminal_source(ray, sourceRadiance);
|
|
5484
|
+
record_deferred_terminal_source(ray, sourceRadiance * segmentTransmittance);
|
|
5170
5485
|
} else {
|
|
5171
|
-
contribution = clamp_sample_radiance(
|
|
5486
|
+
contribution = clamp_sample_radiance(arrivingThroughput * sourceRadiance);
|
|
5172
5487
|
accumulation[ray.rayId] =
|
|
5173
5488
|
accumulation[ray.rayId] + vec4<f32>(contribution * sample_weight(), 1.0);
|
|
5174
5489
|
}
|
|
@@ -5176,7 +5491,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5176
5491
|
return;
|
|
5177
5492
|
}
|
|
5178
5493
|
|
|
5179
|
-
let response = stabilize_surface_path_response(
|
|
5494
|
+
let response = stabilize_surface_path_response(
|
|
5495
|
+
ray,
|
|
5496
|
+
hit,
|
|
5497
|
+
surface_path_response(hit) * segmentTransmittance
|
|
5498
|
+
);
|
|
5180
5499
|
record_deferred_path_response(ray, response);
|
|
5181
5500
|
|
|
5182
5501
|
let shouldEstimateDirectEnvironment =
|
|
@@ -5184,7 +5503,22 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5184
5503
|
hit.material.z >= 0.95 &&
|
|
5185
5504
|
ray.bounce < 2u;
|
|
5186
5505
|
if (shouldEstimateDirectEnvironment) {
|
|
5187
|
-
let directEnvironment = surface_direct_environment_contribution(
|
|
5506
|
+
let directEnvironment = surface_direct_environment_contribution(
|
|
5507
|
+
RayRecord(
|
|
5508
|
+
ray.rayId,
|
|
5509
|
+
ray.parentRayId,
|
|
5510
|
+
ray.sourcePixelId,
|
|
5511
|
+
ray.sampleId,
|
|
5512
|
+
ray.bounce,
|
|
5513
|
+
ray.mediumRefId,
|
|
5514
|
+
ray.flags,
|
|
5515
|
+
0u,
|
|
5516
|
+
ray.origin,
|
|
5517
|
+
ray.direction,
|
|
5518
|
+
vec4<f32>(arrivingThroughput, ray.throughput.w)
|
|
5519
|
+
),
|
|
5520
|
+
hit
|
|
5521
|
+
);
|
|
5188
5522
|
accumulation[ray.rayId] =
|
|
5189
5523
|
accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
|
|
5190
5524
|
}
|
|
@@ -5193,7 +5527,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5193
5527
|
if (deferred_path_resolve_enabled()) {
|
|
5194
5528
|
record_deferred_terminal_source(ray, terminal_surface_environment_source(ray, hit));
|
|
5195
5529
|
} else {
|
|
5196
|
-
let terminalEnvironment = terminal_surface_environment_contribution(
|
|
5530
|
+
let terminalEnvironment = terminal_surface_environment_contribution(
|
|
5531
|
+
ray,
|
|
5532
|
+
arrivingThroughput,
|
|
5533
|
+
hit
|
|
5534
|
+
);
|
|
5197
5535
|
accumulation[ray.rayId] =
|
|
5198
5536
|
accumulation[ray.rayId] + vec4<f32>(terminalEnvironment * sample_weight(), 1.0);
|
|
5199
5537
|
}
|
|
@@ -5208,7 +5546,11 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5208
5546
|
if (deferred_path_resolve_enabled()) {
|
|
5209
5547
|
record_deferred_terminal_source(ray, terminal_surface_environment_source(ray, hit));
|
|
5210
5548
|
} else {
|
|
5211
|
-
let overflowEnvironment = terminal_surface_environment_contribution(
|
|
5549
|
+
let overflowEnvironment = terminal_surface_environment_contribution(
|
|
5550
|
+
ray,
|
|
5551
|
+
arrivingThroughput,
|
|
5552
|
+
hit
|
|
5553
|
+
);
|
|
5212
5554
|
accumulation[ray.rayId] =
|
|
5213
5555
|
accumulation[ray.rayId] + vec4<f32>(overflowEnvironment * sample_weight(), 1.0);
|
|
5214
5556
|
}
|
|
@@ -5222,7 +5564,7 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
5222
5564
|
ray.sourcePixelId,
|
|
5223
5565
|
ray.sampleId,
|
|
5224
5566
|
ray.bounce + 1u,
|
|
5225
|
-
|
|
5567
|
+
scatter.mediumRefId,
|
|
5226
5568
|
scatter.flags,
|
|
5227
5569
|
0u,
|
|
5228
5570
|
vec4<f32>(offset_origin(hit.position.xyz, hit.shadingNormal.xyz), 1.0),
|
|
@@ -5711,6 +6053,11 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5711
6053
|
config.environmentMap,
|
|
5712
6054
|
config.environmentColor
|
|
5713
6055
|
);
|
|
6056
|
+
let mediumTextureResource = createMediumTextureResource(
|
|
6057
|
+
device,
|
|
6058
|
+
constants,
|
|
6059
|
+
config.mediums
|
|
6060
|
+
);
|
|
5714
6061
|
config = Object.freeze({
|
|
5715
6062
|
...config,
|
|
5716
6063
|
environmentMap: Object.freeze({
|
|
@@ -5797,7 +6144,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5797
6144
|
{ binding: 28, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
5798
6145
|
{ binding: 29, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
5799
6146
|
{ binding: 30, visibility: constants.shader.COMPUTE, sampler: { type: "filtering" } },
|
|
5800
|
-
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
6147
|
+
{ binding: 31, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
|
|
6148
|
+
{ binding: 32, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } }
|
|
5801
6149
|
]
|
|
5802
6150
|
});
|
|
5803
6151
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -5984,14 +6332,18 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
5984
6332
|
{ binding: 28, resource: materialAtlasSampler },
|
|
5985
6333
|
{ binding: 29, resource: brdfLutResource.view },
|
|
5986
6334
|
{ binding: 30, resource: brdfLutResource.sampler },
|
|
5987
|
-
{ binding: 31, resource: environmentSamplingResource.view }
|
|
6335
|
+
{ binding: 31, resource: environmentSamplingResource.view },
|
|
6336
|
+
{ binding: 32, resource: mediumTextureResource.view }
|
|
5988
6337
|
]
|
|
5989
6338
|
});
|
|
5990
6339
|
}
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
6340
|
+
function createTraceBindGroups() {
|
|
6341
|
+
return [
|
|
6342
|
+
createTraceBindGroup(activeQueue, nextQueue, "plasius.wavefront.bind.activeNext"),
|
|
6343
|
+
createTraceBindGroup(nextQueue, activeQueue, "plasius.wavefront.bind.nextActive")
|
|
6344
|
+
];
|
|
6345
|
+
}
|
|
6346
|
+
let bindGroups = createTraceBindGroups();
|
|
5995
6347
|
const bvhBuildBindGroup = device.createBindGroup({
|
|
5996
6348
|
label: "plasius.wavefront.bind.bvhBuild",
|
|
5997
6349
|
layout: accelerationBindGroupLayout,
|
|
@@ -6169,6 +6521,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6169
6521
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
6170
6522
|
environmentPortalCount: config.environmentPortalCount,
|
|
6171
6523
|
environmentPortalMode: config.environmentPortalMode,
|
|
6524
|
+
mediumCount: config.mediumCount,
|
|
6172
6525
|
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
6173
6526
|
deferredPathResolve: config.deferredPathResolve,
|
|
6174
6527
|
bvhNodeCount: config.bvhNodeCount,
|
|
@@ -6716,10 +7069,22 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6716
7069
|
...overrides
|
|
6717
7070
|
});
|
|
6718
7071
|
}
|
|
7072
|
+
function rebuildMediumResources(nextConfig) {
|
|
7073
|
+
const previousMediumTextureResource = mediumTextureResource;
|
|
7074
|
+
mediumTextureResource = createMediumTextureResource(device, constants, nextConfig.mediums);
|
|
7075
|
+
bindGroups = createTraceBindGroups();
|
|
7076
|
+
if (previousMediumTextureResource?.ownsTexture) {
|
|
7077
|
+
previousMediumTextureResource.texture?.destroy?.();
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
6719
7080
|
function updateSceneObjects(sceneObjects) {
|
|
6720
7081
|
const nextPackedScene = packWavefrontSceneObjects(sceneObjects, config.sceneObjectCapacity);
|
|
6721
7082
|
packedScene = nextPackedScene;
|
|
6722
|
-
|
|
7083
|
+
const nextConfig = rebuildLiveConfig();
|
|
7084
|
+
if (!mediumTablesEqual(config.mediums, nextConfig.mediums)) {
|
|
7085
|
+
rebuildMediumResources(nextConfig);
|
|
7086
|
+
}
|
|
7087
|
+
config = nextConfig;
|
|
6723
7088
|
device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
|
|
6724
7089
|
return config;
|
|
6725
7090
|
}
|
|
@@ -6743,6 +7108,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6743
7108
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
6744
7109
|
environmentPortalCount: config.environmentPortalCount,
|
|
6745
7110
|
environmentPortalMode: config.environmentPortalMode,
|
|
7111
|
+
mediumCount: config.mediumCount,
|
|
6746
7112
|
environmentMap: createEnvironmentMapSnapshot(config.environmentMap),
|
|
6747
7113
|
deferredPathResolve: config.deferredPathResolve,
|
|
6748
7114
|
bvhNodeCount: config.bvhNodeCount,
|
|
@@ -6783,6 +7149,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
6783
7149
|
if (environmentSamplingResource.ownsTexture) {
|
|
6784
7150
|
environmentSamplingResource.texture?.destroy?.();
|
|
6785
7151
|
}
|
|
7152
|
+
if (mediumTextureResource.ownsTexture) {
|
|
7153
|
+
mediumTextureResource.texture?.destroy?.();
|
|
7154
|
+
}
|
|
6786
7155
|
brdfLutResource.texture?.destroy?.();
|
|
6787
7156
|
if (baseColorAtlasResource.ownsTexture) {
|
|
6788
7157
|
baseColorAtlasResource.texture?.destroy?.();
|