@plasius/gpu-renderer 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +21 -0
- package/dist/index.cjs +643 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +640 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +144 -1
- package/src/index.js +3 -0
- package/src/wavefront-compute.js +684 -73
package/dist/index.cjs
CHANGED
|
@@ -32,11 +32,13 @@ __export(index_exports, {
|
|
|
32
32
|
createWavefrontPathTracingComputeConfig: () => createWavefrontPathTracingComputeConfig,
|
|
33
33
|
createWavefrontPathTracingComputeRenderer: () => createWavefrontPathTracingComputeRenderer,
|
|
34
34
|
createWavefrontPathTracingPlan: () => createWavefrontPathTracingPlan,
|
|
35
|
+
createWavefrontReferenceRay: () => createWavefrontReferenceRay,
|
|
35
36
|
defaultRendererClearColor: () => defaultRendererClearColor,
|
|
36
37
|
defaultRendererWorkerProfile: () => defaultRendererWorkerProfile,
|
|
37
38
|
estimateWavefrontPathTracingMemory: () => estimateWavefrontPathTracingMemory,
|
|
38
39
|
getRendererWorkerManifest: () => getRendererWorkerManifest,
|
|
39
40
|
getRendererWorkerProfile: () => getRendererWorkerProfile,
|
|
41
|
+
intersectWavefrontReferenceTriangle: () => intersectWavefrontReferenceTriangle,
|
|
40
42
|
normalizeWavefrontMesh: () => normalizeWavefrontMesh,
|
|
41
43
|
normalizeWavefrontSceneObject: () => normalizeWavefrontSceneObject,
|
|
42
44
|
packWavefrontBvhNodes: () => packWavefrontBvhNodes,
|
|
@@ -56,6 +58,7 @@ __export(index_exports, {
|
|
|
56
58
|
rendererWorkerQueueClass: () => rendererWorkerQueueClass,
|
|
57
59
|
supportsWavefrontPathTracingCompute: () => supportsWavefrontPathTracingCompute,
|
|
58
60
|
supportsWebGpu: () => supportsWebGpu,
|
|
61
|
+
traceWavefrontReferenceTriangles: () => traceWavefrontReferenceTriangles,
|
|
59
62
|
wavefrontMaterialKinds: () => wavefrontMaterialKinds,
|
|
60
63
|
wavefrontPathTracingComputeLimits: () => wavefrontPathTracingComputeLimits,
|
|
61
64
|
wavefrontSceneObjectKinds: () => wavefrontSceneObjectKinds
|
|
@@ -69,6 +72,7 @@ var DEFAULT_MAX_DEPTH = 6;
|
|
|
69
72
|
var DEFAULT_TILE_SIZE = 128;
|
|
70
73
|
var DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
71
74
|
var DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
75
|
+
var DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
72
76
|
var WORKGROUP_SIZE = 64;
|
|
73
77
|
var RAY_RECORD_BYTES = 80;
|
|
74
78
|
var HIT_RECORD_BYTES = 208;
|
|
@@ -79,9 +83,11 @@ var TRIANGLE_RECORD_BYTES = 208;
|
|
|
79
83
|
var BVH_NODE_RECORD_BYTES = 48;
|
|
80
84
|
var BVH_LEAF_REF_RECORD_BYTES = 16;
|
|
81
85
|
var EMISSIVE_TRIANGLE_INDEX_BYTES = 4;
|
|
86
|
+
var ENVIRONMENT_PORTAL_RECORD_BYTES = 96;
|
|
82
87
|
var ACCUMULATION_RECORD_BYTES = 16;
|
|
83
|
-
var CONFIG_BUFFER_BYTES =
|
|
88
|
+
var CONFIG_BUFFER_BYTES = 272;
|
|
84
89
|
var COUNTER_BUFFER_BYTES = 16;
|
|
90
|
+
var TRACE_STORAGE_BUFFER_BINDINGS = 9;
|
|
85
91
|
var MATERIAL_DIFFUSE = 0;
|
|
86
92
|
var MATERIAL_METAL = 1;
|
|
87
93
|
var MATERIAL_DIELECTRIC = 2;
|
|
@@ -108,6 +114,7 @@ var DEFAULT_ENVIRONMENT_LIGHTING = Object.freeze({
|
|
|
108
114
|
});
|
|
109
115
|
var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
110
116
|
workgroupSize: WORKGROUP_SIZE,
|
|
117
|
+
traceStorageBufferBindings: TRACE_STORAGE_BUFFER_BINDINGS,
|
|
111
118
|
rayRecordBytes: RAY_RECORD_BYTES,
|
|
112
119
|
hitRecordBytes: HIT_RECORD_BYTES,
|
|
113
120
|
sceneObjectRecordBytes: SCENE_OBJECT_RECORD_BYTES,
|
|
@@ -118,6 +125,7 @@ var wavefrontPathTracingComputeLimits = Object.freeze({
|
|
|
118
125
|
bvhLeafReferenceRecordBytes: BVH_LEAF_REF_RECORD_BYTES,
|
|
119
126
|
emissiveTriangleIndexBytes: EMISSIVE_TRIANGLE_INDEX_BYTES,
|
|
120
127
|
emissiveTriangleMetadataRecordBytes: BVH_NODE_RECORD_BYTES,
|
|
128
|
+
environmentPortalRecordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
121
129
|
accumulationRecordBytes: ACCUMULATION_RECORD_BYTES
|
|
122
130
|
});
|
|
123
131
|
var wavefrontSceneObjectKinds = Object.freeze({
|
|
@@ -209,6 +217,9 @@ function subtract(a, b) {
|
|
|
209
217
|
function scale(a, scalar) {
|
|
210
218
|
return [a[0] * scalar, a[1] * scalar, a[2] * scalar];
|
|
211
219
|
}
|
|
220
|
+
function dot(a, b) {
|
|
221
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
222
|
+
}
|
|
212
223
|
function cross(a, b) {
|
|
213
224
|
return [
|
|
214
225
|
a[1] * b[2] - a[2] * b[1],
|
|
@@ -223,6 +234,25 @@ function normalize(value, fallback = [0, 0, 1]) {
|
|
|
223
234
|
}
|
|
224
235
|
return [value[0] / length, value[1] / length, value[2] / length];
|
|
225
236
|
}
|
|
237
|
+
function hashUint32(value) {
|
|
238
|
+
let x = value >>> 0;
|
|
239
|
+
x = ((x >>> 16 ^ x) >>> 0) * 73244475 >>> 0;
|
|
240
|
+
x = ((x >>> 16 ^ x) >>> 0) * 73244475 >>> 0;
|
|
241
|
+
return (x >>> 16 ^ x) >>> 0;
|
|
242
|
+
}
|
|
243
|
+
function mixSeed(pixelId, sampleId, bounce, frameIndex, dimension) {
|
|
244
|
+
let x = (pixelId >>> 0) * 747796405 ^ (sampleId >>> 0) * 2891336453 ^ (bounce >>> 0) * 277803737 ^ (frameIndex >>> 0) * 1442695041 ^ (dimension >>> 0) * 1597334677;
|
|
245
|
+
x >>>= 0;
|
|
246
|
+
x ^= x >>> 16;
|
|
247
|
+
x = x * 2146121005 >>> 0;
|
|
248
|
+
x ^= x >>> 15;
|
|
249
|
+
x = x * 2221713035 >>> 0;
|
|
250
|
+
x ^= x >>> 16;
|
|
251
|
+
return x >>> 0;
|
|
252
|
+
}
|
|
253
|
+
function random01FromSeed(seed) {
|
|
254
|
+
return (hashUint32(seed) & 16777215) / 16777215;
|
|
255
|
+
}
|
|
226
256
|
function getArrayLikeLength(value) {
|
|
227
257
|
return Array.isArray(value) || ArrayBuffer.isView(value) ? value.length : 0;
|
|
228
258
|
}
|
|
@@ -821,6 +851,150 @@ function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
|
821
851
|
exposure: Math.max(1e-4, readFiniteNumber("environmentLighting.exposure", source.exposure, DEFAULT_ENVIRONMENT_LIGHTING.exposure))
|
|
822
852
|
});
|
|
823
853
|
}
|
|
854
|
+
function evaluateReferenceEnvironmentRadiance(config, origin, direction) {
|
|
855
|
+
void origin;
|
|
856
|
+
const rayDirection = normalize(direction, [0, 1, 0]);
|
|
857
|
+
const upFactor = clamp(rayDirection[1] * 0.5 + 0.5, 0, 1);
|
|
858
|
+
const sunDirection = normalize(
|
|
859
|
+
config.environmentLighting?.sunDirection ?? DEFAULT_ENVIRONMENT_LIGHTING.sunDirection,
|
|
860
|
+
DEFAULT_ENVIRONMENT_LIGHTING.sunDirection
|
|
861
|
+
);
|
|
862
|
+
const sunGlow = Math.pow(clamp(dot(rayDirection, sunDirection), 0, 1), 192);
|
|
863
|
+
const horizonColor = config.environmentLighting?.horizonColor ?? DEFAULT_ENVIRONMENT_LIGHTING.horizonColor;
|
|
864
|
+
const zenithColor = config.environmentLighting?.zenithColor ?? DEFAULT_ENVIRONMENT_LIGHTING.zenithColor;
|
|
865
|
+
const sunColor = config.environmentLighting?.sunColor ?? DEFAULT_ENVIRONMENT_LIGHTING.sunColor;
|
|
866
|
+
const intensity = Math.max(
|
|
867
|
+
1e-4,
|
|
868
|
+
Number(config.environmentLighting?.intensity ?? DEFAULT_ENVIRONMENT_LIGHTING.intensity)
|
|
869
|
+
);
|
|
870
|
+
return Object.freeze([
|
|
871
|
+
(horizonColor[0] * (1 - upFactor) + zenithColor[0] * upFactor + sunColor[0] * sunGlow) * intensity,
|
|
872
|
+
(horizonColor[1] * (1 - upFactor) + zenithColor[1] * upFactor + sunColor[1] * sunGlow) * intensity,
|
|
873
|
+
(horizonColor[2] * (1 - upFactor) + zenithColor[2] * upFactor + sunColor[2] * sunGlow) * intensity,
|
|
874
|
+
1
|
|
875
|
+
]);
|
|
876
|
+
}
|
|
877
|
+
function resolveEnvironmentPortalMode(value, hasPortals) {
|
|
878
|
+
if (value === void 0 || value === null) {
|
|
879
|
+
return hasPortals ? 2 : 0;
|
|
880
|
+
}
|
|
881
|
+
if (Number.isInteger(value) && value >= 0 && value <= 2) {
|
|
882
|
+
return value;
|
|
883
|
+
}
|
|
884
|
+
if (value === "disabled") {
|
|
885
|
+
return 0;
|
|
886
|
+
}
|
|
887
|
+
if (value === "guide") {
|
|
888
|
+
return 1;
|
|
889
|
+
}
|
|
890
|
+
if (value === "guide-and-gate" || value === "gate") {
|
|
891
|
+
return 2;
|
|
892
|
+
}
|
|
893
|
+
throw new Error(
|
|
894
|
+
"environmentPortalMode must be disabled, guide, guide-and-gate, or an integer between 0 and 2."
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
function orthogonalPortalTangent(normal) {
|
|
898
|
+
if (Math.abs(normal[1]) < 0.92) {
|
|
899
|
+
return normalize(cross([0, 1, 0], normal), [1, 0, 0]);
|
|
900
|
+
}
|
|
901
|
+
return normalize(cross([1, 0, 0], normal), [0, 0, 1]);
|
|
902
|
+
}
|
|
903
|
+
function resolvePortalTangent(value, normal) {
|
|
904
|
+
const fallback = orthogonalPortalTangent(normal);
|
|
905
|
+
const tangent = asUnitVec3(value, fallback);
|
|
906
|
+
const projected = subtract(tangent, scale(normal, dot(tangent, normal)));
|
|
907
|
+
return normalize(projected, fallback);
|
|
908
|
+
}
|
|
909
|
+
function readPositiveFiniteNumber(name, value, fallback) {
|
|
910
|
+
const numeric = readFiniteNumber(name, value, fallback);
|
|
911
|
+
if (numeric <= 0) {
|
|
912
|
+
throw new Error(`${name} must be a positive finite number.`);
|
|
913
|
+
}
|
|
914
|
+
return numeric;
|
|
915
|
+
}
|
|
916
|
+
function readPortalExtent(name, value, halfName, halfValue) {
|
|
917
|
+
if (value !== void 0 && value !== null) {
|
|
918
|
+
return readPositiveFiniteNumber(name, value, 1);
|
|
919
|
+
}
|
|
920
|
+
return readPositiveFiniteNumber(halfName, halfValue, 0.5) * 2;
|
|
921
|
+
}
|
|
922
|
+
function normalizeEnvironmentPortal(portal, index) {
|
|
923
|
+
if (!portal || typeof portal !== "object") {
|
|
924
|
+
throw new Error(`environmentPortals[${index}] must be an object.`);
|
|
925
|
+
}
|
|
926
|
+
const shape = portal.shape ?? portal.kind ?? "rectangle";
|
|
927
|
+
if (shape !== "rectangle") {
|
|
928
|
+
throw new Error(`environmentPortals[${index}].shape must be "rectangle".`);
|
|
929
|
+
}
|
|
930
|
+
const position = asVec3(portal.position ?? portal.center, [0, 0, 0]);
|
|
931
|
+
const normal = asUnitVec3(portal.normal, [0, 0, 1]);
|
|
932
|
+
const tangent = resolvePortalTangent(portal.tangent, normal);
|
|
933
|
+
const bitangent = normalize(cross(normal, tangent), [0, 1, 0]);
|
|
934
|
+
const width = readPortalExtent(
|
|
935
|
+
`environmentPortals[${index}].width`,
|
|
936
|
+
portal.width,
|
|
937
|
+
`environmentPortals[${index}].halfWidth`,
|
|
938
|
+
portal.halfWidth
|
|
939
|
+
);
|
|
940
|
+
const height = readPortalExtent(
|
|
941
|
+
`environmentPortals[${index}].height`,
|
|
942
|
+
portal.height,
|
|
943
|
+
`environmentPortals[${index}].halfHeight`,
|
|
944
|
+
portal.halfHeight
|
|
945
|
+
);
|
|
946
|
+
const radianceScale = Math.max(
|
|
947
|
+
0,
|
|
948
|
+
readFiniteNumber(
|
|
949
|
+
`environmentPortals[${index}].radianceScale`,
|
|
950
|
+
portal.radianceScale ?? portal.intensity,
|
|
951
|
+
1
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
return Object.freeze({
|
|
955
|
+
kind: 1,
|
|
956
|
+
flags: portal.twoSided === false ? 0 : 1,
|
|
957
|
+
position: Object.freeze([position[0], position[1], position[2], width * height]),
|
|
958
|
+
normal: Object.freeze([normal[0], normal[1], normal[2], radianceScale]),
|
|
959
|
+
tangent: Object.freeze([tangent[0], tangent[1], tangent[2], width * 0.5]),
|
|
960
|
+
bitangent: Object.freeze([bitangent[0], bitangent[1], bitangent[2], height * 0.5]),
|
|
961
|
+
color: Object.freeze(asColor(portal.color, [1, 1, 1, 1]))
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
function normalizeEnvironmentPortals(value) {
|
|
965
|
+
if (value === void 0 || value === null) {
|
|
966
|
+
return Object.freeze([]);
|
|
967
|
+
}
|
|
968
|
+
if (!Array.isArray(value)) {
|
|
969
|
+
throw new Error("environmentPortals must be an array when provided.");
|
|
970
|
+
}
|
|
971
|
+
return Object.freeze(value.map(normalizeEnvironmentPortal));
|
|
972
|
+
}
|
|
973
|
+
function packEnvironmentPortals(portals, capacity) {
|
|
974
|
+
const bytes = new ArrayBuffer(capacity * ENVIRONMENT_PORTAL_RECORD_BYTES);
|
|
975
|
+
const data = new DataView(bytes);
|
|
976
|
+
const floatView = new Float32Array(bytes);
|
|
977
|
+
portals.forEach((portal, index) => {
|
|
978
|
+
const byteOffset = index * ENVIRONMENT_PORTAL_RECORD_BYTES;
|
|
979
|
+
const floatOffset = byteOffset / Float32Array.BYTES_PER_ELEMENT;
|
|
980
|
+
data.setUint32(byteOffset, portal.kind, true);
|
|
981
|
+
data.setUint32(byteOffset + 4, portal.flags, true);
|
|
982
|
+
data.setUint32(byteOffset + 8, 0, true);
|
|
983
|
+
data.setUint32(byteOffset + 12, 0, true);
|
|
984
|
+
writeVec4(floatView, floatOffset + 4, portal.position);
|
|
985
|
+
writeVec4(floatView, floatOffset + 8, portal.normal);
|
|
986
|
+
writeVec4(floatView, floatOffset + 12, portal.tangent);
|
|
987
|
+
writeVec4(floatView, floatOffset + 16, portal.bitangent);
|
|
988
|
+
writeVec4(floatView, floatOffset + 20, portal.color);
|
|
989
|
+
});
|
|
990
|
+
return Object.freeze({
|
|
991
|
+
buffer: bytes,
|
|
992
|
+
portals,
|
|
993
|
+
count: portals.length,
|
|
994
|
+
capacity,
|
|
995
|
+
recordBytes: ENVIRONMENT_PORTAL_RECORD_BYTES
|
|
996
|
+
});
|
|
997
|
+
}
|
|
824
998
|
function getCanvasDimension(canvas, key, fallback) {
|
|
825
999
|
const value = Number(canvas?.[key]);
|
|
826
1000
|
if (Number.isFinite(value) && value > 0) {
|
|
@@ -876,6 +1050,11 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
876
1050
|
options.emissiveTriangleCapacity,
|
|
877
1051
|
0
|
|
878
1052
|
);
|
|
1053
|
+
const environmentPortalCapacity = readNonNegativeInteger(
|
|
1054
|
+
"environmentPortalCapacity",
|
|
1055
|
+
options.environmentPortalCapacity,
|
|
1056
|
+
0
|
|
1057
|
+
);
|
|
879
1058
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
880
1059
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
881
1060
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
@@ -884,6 +1063,7 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
884
1063
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
885
1064
|
const bvhLeafReferenceBytes = bvhLeafSortCapacity * BVH_LEAF_REF_RECORD_BYTES;
|
|
886
1065
|
const emissiveTriangleMetadataBytes = emissiveTriangleCapacity * BVH_NODE_RECORD_BYTES;
|
|
1066
|
+
const environmentPortalBytes = environmentPortalCapacity * ENVIRONMENT_PORTAL_RECORD_BYTES;
|
|
887
1067
|
return Object.freeze({
|
|
888
1068
|
queueBytes,
|
|
889
1069
|
queuePairBytes: queueBytes * 2,
|
|
@@ -894,9 +1074,10 @@ function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
894
1074
|
bvhNodeBytes,
|
|
895
1075
|
bvhLeafReferenceBytes,
|
|
896
1076
|
emissiveTriangleMetadataBytes,
|
|
1077
|
+
environmentPortalBytes,
|
|
897
1078
|
configBytes: CONFIG_BUFFER_BYTES,
|
|
898
1079
|
counterBytes: COUNTER_BUFFER_BYTES,
|
|
899
|
-
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES
|
|
1080
|
+
totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + sceneObjectBytes + triangleBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES
|
|
900
1081
|
});
|
|
901
1082
|
}
|
|
902
1083
|
function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
@@ -957,6 +1138,21 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
957
1138
|
environmentColor,
|
|
958
1139
|
ambientColor
|
|
959
1140
|
);
|
|
1141
|
+
const environmentPortals = normalizeEnvironmentPortals(
|
|
1142
|
+
options.environmentPortals ?? options.environmentLightPortals ?? options.environmentLighting?.environmentPortals
|
|
1143
|
+
);
|
|
1144
|
+
const environmentPortalCapacity = Math.max(
|
|
1145
|
+
environmentPortals.length,
|
|
1146
|
+
readNonNegativeInteger(
|
|
1147
|
+
"environmentPortalCapacity",
|
|
1148
|
+
options.environmentPortalCapacity,
|
|
1149
|
+
DEFAULT_ENVIRONMENT_PORTAL_CAPACITY
|
|
1150
|
+
)
|
|
1151
|
+
);
|
|
1152
|
+
const environmentPortalMode = resolveEnvironmentPortalMode(
|
|
1153
|
+
options.environmentPortalMode ?? options.portalMode ?? options.environmentLighting?.environmentPortalMode,
|
|
1154
|
+
environmentPortals.length > 0
|
|
1155
|
+
);
|
|
960
1156
|
return Object.freeze({
|
|
961
1157
|
width,
|
|
962
1158
|
height,
|
|
@@ -985,6 +1181,10 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
985
1181
|
environmentColor: environmentLighting.environmentColor,
|
|
986
1182
|
ambientColor: environmentLighting.ambientColor,
|
|
987
1183
|
environmentLighting,
|
|
1184
|
+
environmentPortals,
|
|
1185
|
+
environmentPortalCount: environmentPortals.length,
|
|
1186
|
+
environmentPortalCapacity,
|
|
1187
|
+
environmentPortalMode,
|
|
988
1188
|
displayQuality: options.displayQuality === true,
|
|
989
1189
|
requiresMeshBvhForDisplayQuality: true,
|
|
990
1190
|
denoise: options.denoise !== false,
|
|
@@ -995,7 +1195,8 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
995
1195
|
triangleCapacity,
|
|
996
1196
|
bvhNodeCapacity,
|
|
997
1197
|
bvhLeafSortCapacity,
|
|
998
|
-
emissiveTriangleCapacity: emissiveTriangleIndices.capacity
|
|
1198
|
+
emissiveTriangleCapacity: emissiveTriangleIndices.capacity,
|
|
1199
|
+
environmentPortalCapacity
|
|
999
1200
|
})
|
|
1000
1201
|
});
|
|
1001
1202
|
}
|
|
@@ -1179,6 +1380,10 @@ function createConfigPayload(config, tile, frameIndex, buildRange = {}) {
|
|
|
1179
1380
|
data.setUint32(244, buildRange.count ?? 0, true);
|
|
1180
1381
|
data.setUint32(248, buildRange.sortItemCount ?? 0, true);
|
|
1181
1382
|
data.setUint32(252, config.emissiveTriangleCount ?? 0, true);
|
|
1383
|
+
data.setUint32(256, config.environmentPortalCount ?? 0, true);
|
|
1384
|
+
data.setUint32(260, config.environmentPortalMode ?? 0, true);
|
|
1385
|
+
data.setUint32(264, 0, true);
|
|
1386
|
+
data.setUint32(268, 0, true);
|
|
1182
1387
|
return bytes;
|
|
1183
1388
|
}
|
|
1184
1389
|
function createTiles(width, height, tileSize) {
|
|
@@ -1197,6 +1402,209 @@ function createTiles(width, height, tileSize) {
|
|
|
1197
1402
|
}
|
|
1198
1403
|
return Object.freeze(tiles);
|
|
1199
1404
|
}
|
|
1405
|
+
function normalizeReferenceTile(config, tileInput = {}) {
|
|
1406
|
+
const tileX = clamp(
|
|
1407
|
+
readNonNegativeInteger("tile.x", tileInput.x, 0),
|
|
1408
|
+
0,
|
|
1409
|
+
Math.max(0, config.width - 1)
|
|
1410
|
+
);
|
|
1411
|
+
const tileY = clamp(
|
|
1412
|
+
readNonNegativeInteger("tile.y", tileInput.y, 0),
|
|
1413
|
+
0,
|
|
1414
|
+
Math.max(0, config.height - 1)
|
|
1415
|
+
);
|
|
1416
|
+
const tileWidth = clamp(
|
|
1417
|
+
readPositiveInteger("tile.width", tileInput.width, config.width - tileX),
|
|
1418
|
+
1,
|
|
1419
|
+
config.width - tileX
|
|
1420
|
+
);
|
|
1421
|
+
const tileHeight = clamp(
|
|
1422
|
+
readPositiveInteger("tile.height", tileInput.height, config.height - tileY),
|
|
1423
|
+
1,
|
|
1424
|
+
config.height - tileY
|
|
1425
|
+
);
|
|
1426
|
+
return Object.freeze({
|
|
1427
|
+
x: tileX,
|
|
1428
|
+
y: tileY,
|
|
1429
|
+
width: tileWidth,
|
|
1430
|
+
height: tileHeight
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
function repairReferenceShadingNormal(geometricNormal, shadingNormal) {
|
|
1434
|
+
const normal = normalize(shadingNormal, geometricNormal);
|
|
1435
|
+
return dot(normal, geometricNormal) < 0 ? scale(normal, -1) : normal;
|
|
1436
|
+
}
|
|
1437
|
+
function readOptionalMaxDistance(value) {
|
|
1438
|
+
if (value === void 0 || value === null) {
|
|
1439
|
+
return Number.POSITIVE_INFINITY;
|
|
1440
|
+
}
|
|
1441
|
+
const numeric = Number(value);
|
|
1442
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
1443
|
+
throw new Error("maxDistance must be a positive finite number when provided.");
|
|
1444
|
+
}
|
|
1445
|
+
return numeric;
|
|
1446
|
+
}
|
|
1447
|
+
function createWavefrontReferenceRay(config, options = {}) {
|
|
1448
|
+
if (!config || typeof config !== "object") {
|
|
1449
|
+
throw new Error("config must be a wavefront path tracing config.");
|
|
1450
|
+
}
|
|
1451
|
+
const tile = normalizeReferenceTile(config, options.tile);
|
|
1452
|
+
const tilePixelCount = tile.width * tile.height;
|
|
1453
|
+
const pixelIndex = readNonNegativeInteger("pixelIndex", options.pixelIndex, 0);
|
|
1454
|
+
if (pixelIndex >= tilePixelCount) {
|
|
1455
|
+
throw new Error(`pixelIndex ${pixelIndex} exceeds tile capacity ${tilePixelCount}.`);
|
|
1456
|
+
}
|
|
1457
|
+
const sampleIndex = readNonNegativeInteger("sampleIndex", options.sampleIndex, 0);
|
|
1458
|
+
const frameIndex = readNonNegativeInteger("frameIndex", options.frameIndex, config.frameIndex ?? 0);
|
|
1459
|
+
const jitterScale = clamp(readFiniteNumber("jitterScale", options.jitterScale, 0.35), 0, 1);
|
|
1460
|
+
const localX = pixelIndex % tile.width;
|
|
1461
|
+
const localY = Math.floor(pixelIndex / tile.width);
|
|
1462
|
+
const pixelX = tile.x + localX;
|
|
1463
|
+
const pixelY = tile.y + localY;
|
|
1464
|
+
const sourcePixelId = pixelY * config.width + pixelX;
|
|
1465
|
+
const jitterX = random01FromSeed(mixSeed(sourcePixelId, sampleIndex, 0, frameIndex, 1)) - 0.5;
|
|
1466
|
+
const jitterY = random01FromSeed(mixSeed(sourcePixelId, sampleIndex, 0, frameIndex, 2)) - 0.5;
|
|
1467
|
+
const ndcX = (pixelX + 0.5 + jitterX * jitterScale) / config.width * 2 - 1;
|
|
1468
|
+
const ndcY = 1 - (pixelY + 0.5 + jitterY * jitterScale) / config.height * 2;
|
|
1469
|
+
const viewX = ndcX * config.camera.tanHalfFovY * config.camera.aspect;
|
|
1470
|
+
const viewY = ndcY * config.camera.tanHalfFovY;
|
|
1471
|
+
const direction = normalize(
|
|
1472
|
+
add(
|
|
1473
|
+
add(config.camera.forward, scale(config.camera.right, viewX)),
|
|
1474
|
+
scale(config.camera.up, viewY)
|
|
1475
|
+
),
|
|
1476
|
+
config.camera.forward
|
|
1477
|
+
);
|
|
1478
|
+
return Object.freeze({
|
|
1479
|
+
rayId: pixelIndex,
|
|
1480
|
+
parentRayId: 4294967295,
|
|
1481
|
+
sourcePixelId,
|
|
1482
|
+
sampleId: sampleIndex,
|
|
1483
|
+
bounce: 0,
|
|
1484
|
+
mediumRefId: 0,
|
|
1485
|
+
flags: 0,
|
|
1486
|
+
origin: Object.freeze([...config.camera.position]),
|
|
1487
|
+
direction: Object.freeze(direction),
|
|
1488
|
+
throughput: Object.freeze([1, 1, 1, 1]),
|
|
1489
|
+
pixelX,
|
|
1490
|
+
pixelY
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
function intersectWavefrontReferenceTriangle(ray, triangle, options = {}) {
|
|
1494
|
+
if (!ray || typeof ray !== "object") {
|
|
1495
|
+
throw new Error("ray must be a wavefront reference ray.");
|
|
1496
|
+
}
|
|
1497
|
+
if (!triangle || typeof triangle !== "object") {
|
|
1498
|
+
throw new Error("triangle must be a wavefront triangle record.");
|
|
1499
|
+
}
|
|
1500
|
+
const maxDistance = readOptionalMaxDistance(options.maxDistance);
|
|
1501
|
+
const triangleIndex = readNonNegativeInteger("triangleIndex", options.triangleIndex, 0);
|
|
1502
|
+
const edge1 = subtract(triangle.v1, triangle.v0);
|
|
1503
|
+
const edge2 = subtract(triangle.v2, triangle.v0);
|
|
1504
|
+
const pvec = cross(ray.direction, edge2);
|
|
1505
|
+
const determinant = dot(edge1, pvec);
|
|
1506
|
+
if (Math.abs(determinant) < 1e-7) {
|
|
1507
|
+
return null;
|
|
1508
|
+
}
|
|
1509
|
+
const invDet = 1 / determinant;
|
|
1510
|
+
const tvec = subtract(ray.origin, triangle.v0);
|
|
1511
|
+
const u = dot(tvec, pvec) * invDet;
|
|
1512
|
+
if (u < 0 || u > 1) {
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
const qvec = cross(tvec, edge1);
|
|
1516
|
+
const v = dot(ray.direction, qvec) * invDet;
|
|
1517
|
+
if (v < 0 || u + v > 1) {
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
const distance = dot(edge2, qvec) * invDet;
|
|
1521
|
+
if (distance <= 1e-3 || distance > maxDistance) {
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
const geometric = normalize(cross(edge1, edge2), [0, 1, 0]);
|
|
1525
|
+
const frontFace = dot(ray.direction, geometric) < 0;
|
|
1526
|
+
const orientedGeometric = frontFace ? geometric : scale(geometric, -1);
|
|
1527
|
+
const w = 1 - u - v;
|
|
1528
|
+
const interpolated = [
|
|
1529
|
+
triangle.n0[0] * w + triangle.n1[0] * u + triangle.n2[0] * v,
|
|
1530
|
+
triangle.n0[1] * w + triangle.n1[1] * u + triangle.n2[1] * v,
|
|
1531
|
+
triangle.n0[2] * w + triangle.n1[2] * u + triangle.n2[2] * v
|
|
1532
|
+
];
|
|
1533
|
+
const shadingNormal = repairReferenceShadingNormal(orientedGeometric, interpolated);
|
|
1534
|
+
const uv = [
|
|
1535
|
+
triangle.uv0[0] * w + triangle.uv1[0] * u + triangle.uv2[0] * v,
|
|
1536
|
+
triangle.uv0[1] * w + triangle.uv1[1] * u + triangle.uv2[1] * v
|
|
1537
|
+
];
|
|
1538
|
+
const position = add(ray.origin, scale(ray.direction, distance));
|
|
1539
|
+
return Object.freeze({
|
|
1540
|
+
hitType: "surface",
|
|
1541
|
+
rayId: ray.rayId,
|
|
1542
|
+
sourcePixelId: ray.sourcePixelId,
|
|
1543
|
+
distance,
|
|
1544
|
+
entityId: triangle.meshId,
|
|
1545
|
+
instanceId: 0,
|
|
1546
|
+
primitiveId: triangle.triangleId,
|
|
1547
|
+
materialId: triangle.materialKind,
|
|
1548
|
+
materialRefId: triangle.materialRefId,
|
|
1549
|
+
mediumRefId: triangle.mediumRefId,
|
|
1550
|
+
barycentrics: Object.freeze([w, u, v]),
|
|
1551
|
+
uv: Object.freeze(uv),
|
|
1552
|
+
geometricNormal: Object.freeze(orientedGeometric),
|
|
1553
|
+
shadingNormal: Object.freeze(shadingNormal),
|
|
1554
|
+
frontFace,
|
|
1555
|
+
triangleIndex,
|
|
1556
|
+
triangleId: triangle.triangleId,
|
|
1557
|
+
position: Object.freeze(position),
|
|
1558
|
+
color: triangle.color,
|
|
1559
|
+
emission: triangle.emission,
|
|
1560
|
+
material: triangle.material
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
function createWavefrontReferenceEnvironmentHit(config, ray) {
|
|
1564
|
+
const radiance = evaluateReferenceEnvironmentRadiance(config, ray.origin, ray.direction);
|
|
1565
|
+
return Object.freeze({
|
|
1566
|
+
hitType: "environment",
|
|
1567
|
+
rayId: ray.rayId,
|
|
1568
|
+
sourcePixelId: ray.sourcePixelId,
|
|
1569
|
+
distance: -1,
|
|
1570
|
+
entityId: 0,
|
|
1571
|
+
instanceId: 0,
|
|
1572
|
+
primitiveId: 0,
|
|
1573
|
+
materialId: 0,
|
|
1574
|
+
materialRefId: 0,
|
|
1575
|
+
mediumRefId: 0,
|
|
1576
|
+
barycentrics: Object.freeze([0, 0, 0]),
|
|
1577
|
+
uv: Object.freeze([0, 0]),
|
|
1578
|
+
geometricNormal: Object.freeze(scale(ray.direction, -1)),
|
|
1579
|
+
shadingNormal: Object.freeze(scale(ray.direction, -1)),
|
|
1580
|
+
frontFace: true,
|
|
1581
|
+
triangleIndex: -1,
|
|
1582
|
+
triangleId: -1,
|
|
1583
|
+
position: Object.freeze(add(ray.origin, scale(ray.direction, 1e3))),
|
|
1584
|
+
color: Object.freeze([0, 0, 0, 0]),
|
|
1585
|
+
emission: radiance,
|
|
1586
|
+
material: Object.freeze([1, 0, 1, 1])
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
function traceWavefrontReferenceTriangles(config, ray, triangles, options = {}) {
|
|
1590
|
+
if (!config || typeof config !== "object") {
|
|
1591
|
+
throw new Error("config must be a wavefront path tracing config.");
|
|
1592
|
+
}
|
|
1593
|
+
const source = Array.isArray(triangles) ? triangles : [];
|
|
1594
|
+
let nearestHit = null;
|
|
1595
|
+
let nearestDistance = readOptionalMaxDistance(options.maxDistance);
|
|
1596
|
+
source.forEach((triangle, index) => {
|
|
1597
|
+
const hit = intersectWavefrontReferenceTriangle(ray, triangle, {
|
|
1598
|
+
maxDistance: Number.isFinite(nearestDistance) ? nearestDistance : void 0,
|
|
1599
|
+
triangleIndex: index
|
|
1600
|
+
});
|
|
1601
|
+
if (hit && hit.distance < nearestDistance) {
|
|
1602
|
+
nearestDistance = hit.distance;
|
|
1603
|
+
nearestHit = hit;
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
return nearestHit ?? createWavefrontReferenceEnvironmentHit(config, ray);
|
|
1607
|
+
}
|
|
1200
1608
|
function clampTileSizeForDevice(config, device) {
|
|
1201
1609
|
const limit = Number(device?.limits?.maxStorageBufferBindingSize);
|
|
1202
1610
|
if (!Number.isFinite(limit) || limit <= 0) {
|
|
@@ -1425,6 +1833,10 @@ struct FrameConfig {
|
|
|
1425
1833
|
bvhBuildNodeCount: u32,
|
|
1426
1834
|
bvhSortItemCount: u32,
|
|
1427
1835
|
emissiveTriangleCount: u32,
|
|
1836
|
+
environmentPortalCount: u32,
|
|
1837
|
+
environmentPortalMode: u32,
|
|
1838
|
+
_portalPad0: u32,
|
|
1839
|
+
_portalPad1: u32,
|
|
1428
1840
|
};
|
|
1429
1841
|
|
|
1430
1842
|
struct Counters {
|
|
@@ -1448,6 +1860,18 @@ struct Candidate {
|
|
|
1448
1860
|
mediumRefId: u32,
|
|
1449
1861
|
};
|
|
1450
1862
|
|
|
1863
|
+
struct EnvironmentPortal {
|
|
1864
|
+
kind: u32,
|
|
1865
|
+
flags: u32,
|
|
1866
|
+
_pad0: u32,
|
|
1867
|
+
_pad1: u32,
|
|
1868
|
+
position: vec4<f32>,
|
|
1869
|
+
normal: vec4<f32>,
|
|
1870
|
+
tangent: vec4<f32>,
|
|
1871
|
+
bitangent: vec4<f32>,
|
|
1872
|
+
color: vec4<f32>,
|
|
1873
|
+
};
|
|
1874
|
+
|
|
1451
1875
|
@group(0) @binding(0) var<storage, read_write> activeQueue: array<RayRecord>;
|
|
1452
1876
|
@group(0) @binding(1) var<storage, read_write> nextQueue: array<RayRecord>;
|
|
1453
1877
|
@group(0) @binding(2) var<storage, read_write> hits: array<HitRecord>;
|
|
@@ -1467,6 +1891,7 @@ struct Candidate {
|
|
|
1467
1891
|
@group(0) @binding(16) var radianceImage: texture_storage_2d<rgba16float, write>;
|
|
1468
1892
|
@group(0) @binding(17) var finalDenoiseInputRadiance: texture_2d<f32>;
|
|
1469
1893
|
@group(0) @binding(18) var denoisedOutputImage: texture_storage_2d<rgba8unorm, write>;
|
|
1894
|
+
@group(0) @binding(19) var<storage, read> environmentPortals: array<EnvironmentPortal>;
|
|
1470
1895
|
|
|
1471
1896
|
fn hash_u32(value: u32) -> u32 {
|
|
1472
1897
|
var x = value;
|
|
@@ -1507,7 +1932,48 @@ fn saturate(value: f32) -> f32 {
|
|
|
1507
1932
|
return clamp(value, 0.0, 1.0);
|
|
1508
1933
|
}
|
|
1509
1934
|
|
|
1510
|
-
fn
|
|
1935
|
+
fn max_component(value: vec3<f32>) -> f32 {
|
|
1936
|
+
return max(max(value.x, value.y), value.z);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
fn environment_portal_radiance_scale(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1940
|
+
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
1941
|
+
return vec3<f32>(1.0);
|
|
1942
|
+
}
|
|
1943
|
+
var scale = vec3<f32>(0.0);
|
|
1944
|
+
for (var portalIndex = 0u; portalIndex < config.environmentPortalCount; portalIndex = portalIndex + 1u) {
|
|
1945
|
+
let portal = environmentPortals[portalIndex];
|
|
1946
|
+
if (portal.kind == 1u) {
|
|
1947
|
+
let portalNormal = safe_normalize(portal.normal.xyz, vec3<f32>(0.0, 0.0, 1.0));
|
|
1948
|
+
let denominator = dot(direction, portalNormal);
|
|
1949
|
+
let twoSided = (portal.flags & 1u) != 0u;
|
|
1950
|
+
var facing = abs(denominator) > 0.0001;
|
|
1951
|
+
if (!twoSided && denominator <= 0.0001) {
|
|
1952
|
+
facing = false;
|
|
1953
|
+
}
|
|
1954
|
+
if (facing) {
|
|
1955
|
+
let distance = dot(portal.position.xyz - origin, portalNormal) / denominator;
|
|
1956
|
+
if (distance > 0.001) {
|
|
1957
|
+
let hitPosition = origin + direction * distance;
|
|
1958
|
+
let local = hitPosition - portal.position.xyz;
|
|
1959
|
+
let tangent = safe_normalize(portal.tangent.xyz, vec3<f32>(1.0, 0.0, 0.0));
|
|
1960
|
+
let bitangent = safe_normalize(portal.bitangent.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
1961
|
+
let u = dot(local, tangent);
|
|
1962
|
+
let v = dot(local, bitangent);
|
|
1963
|
+
if (abs(u) <= portal.tangent.w && abs(v) <= portal.bitangent.w) {
|
|
1964
|
+
let areaWeight = clamp(sqrt(max(portal.position.w, 0.0001)), 0.25, 4.0);
|
|
1965
|
+
let angleWeight = max(abs(denominator), 0.08);
|
|
1966
|
+
let portalScale = portal.color.rgb * portal.normal.w * portal.color.a * areaWeight * angleWeight;
|
|
1967
|
+
scale = max(scale, portalScale);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return scale;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
fn environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1511
1977
|
let rayDirection = safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0));
|
|
1512
1978
|
let upFactor = saturate(rayDirection.y * 0.5 + 0.5);
|
|
1513
1979
|
let sunDirection = safe_normalize(
|
|
@@ -1518,10 +1984,26 @@ fn environment_radiance(direction: vec3<f32>) -> vec3<f32> {
|
|
|
1518
1984
|
let gradient =
|
|
1519
1985
|
config.environmentHorizonColor.xyz * (1.0 - upFactor) +
|
|
1520
1986
|
config.environmentZenithColor.xyz * upFactor;
|
|
1987
|
+
let portalScale = environment_portal_radiance_scale(origin, rayDirection);
|
|
1988
|
+
let portalHit = max_component(portalScale) > 0.0001;
|
|
1521
1989
|
return (
|
|
1522
1990
|
gradient +
|
|
1523
1991
|
config.environmentSunColor.xyz * sunGlow
|
|
1524
|
-
) *
|
|
1992
|
+
) *
|
|
1993
|
+
max(config.environmentSunDirectionIntensity.w, 0.0001) *
|
|
1994
|
+
select(vec3<f32>(1.0), portalScale, portalHit);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
fn gated_environment_radiance(origin: vec3<f32>, direction: vec3<f32>) -> vec3<f32> {
|
|
1998
|
+
let portalScale = environment_portal_radiance_scale(origin, safe_normalize(direction, vec3<f32>(0.0, 1.0, 0.0)));
|
|
1999
|
+
if (
|
|
2000
|
+
config.environmentPortalCount > 0u &&
|
|
2001
|
+
config.environmentPortalMode == 2u &&
|
|
2002
|
+
max_component(portalScale) <= 0.0001
|
|
2003
|
+
) {
|
|
2004
|
+
return config.ambientColor.xyz * 0.65;
|
|
2005
|
+
}
|
|
2006
|
+
return environment_radiance(origin, direction);
|
|
1525
2007
|
}
|
|
1526
2008
|
|
|
1527
2009
|
fn default_mesh_range() -> MeshRange {
|
|
@@ -1792,7 +2274,7 @@ fn make_ray(pixelIndex: u32) -> RayRecord {
|
|
|
1792
2274
|
}
|
|
1793
2275
|
|
|
1794
2276
|
fn make_miss(ray: RayRecord) -> HitRecord {
|
|
1795
|
-
let radiance =
|
|
2277
|
+
let radiance = gated_environment_radiance(ray.origin.xyz, ray.direction.xyz);
|
|
1796
2278
|
return HitRecord(
|
|
1797
2279
|
ray.rayId,
|
|
1798
2280
|
ray.sourcePixelId,
|
|
@@ -2278,6 +2760,21 @@ fn sample_emissive_triangle_direction(hit: HitRecord, seed: u32, fallback: vec3<
|
|
|
2278
2760
|
return safe_normalize(lightPoint - hit.position.xyz, fallback);
|
|
2279
2761
|
}
|
|
2280
2762
|
|
|
2763
|
+
fn sample_environment_portal_direction(hit: HitRecord, seed: u32, fallback: vec3<f32>) -> vec3<f32> {
|
|
2764
|
+
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
2765
|
+
return fallback;
|
|
2766
|
+
}
|
|
2767
|
+
let portalSlot = min(
|
|
2768
|
+
u32(random01(seed + 211u) * f32(config.environmentPortalCount)),
|
|
2769
|
+
config.environmentPortalCount - 1u
|
|
2770
|
+
);
|
|
2771
|
+
let portal = environmentPortals[portalSlot];
|
|
2772
|
+
let u = (random01(seed + 223u) * 2.0 - 1.0) * portal.tangent.w;
|
|
2773
|
+
let v = (random01(seed + 227u) * 2.0 - 1.0) * portal.bitangent.w;
|
|
2774
|
+
let portalTarget = portal.position.xyz + portal.tangent.xyz * u + portal.bitangent.xyz * v;
|
|
2775
|
+
return safe_normalize(portalTarget - hit.position.xyz, fallback);
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2281
2778
|
fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult {
|
|
2282
2779
|
let roughness = clamp(hit.material.x, 0.0, 1.0);
|
|
2283
2780
|
if (hit.materialKind == 1u) {
|
|
@@ -2317,8 +2814,17 @@ fn scatter_direction(ray: RayRecord, hit: HitRecord, seed: u32) -> ScatterResult
|
|
|
2317
2814
|
let canSampleLight = dot(hit.shadingNormal.xyz, guidedLight) > -0.04;
|
|
2318
2815
|
let guideProbability = select(0.38, 0.72, ray.bounce == 0u);
|
|
2319
2816
|
let useGuidedLight = canSampleLight && random01(seed + 37u) < guideProbability;
|
|
2817
|
+
let guidedPortal = sample_environment_portal_direction(hit, seed, randomDiffuse);
|
|
2818
|
+
let canSamplePortal = dot(hit.shadingNormal.xyz, guidedPortal) > -0.04;
|
|
2819
|
+
let useGuidedPortal =
|
|
2820
|
+
!useGuidedLight &&
|
|
2821
|
+
canSamplePortal &&
|
|
2822
|
+
config.environmentPortalCount > 0u &&
|
|
2823
|
+
config.environmentPortalMode > 0u &&
|
|
2824
|
+
random01(seed + 89u) < 0.58;
|
|
2825
|
+
let guidedDirection = select(randomDiffuse, guidedPortal, useGuidedPortal);
|
|
2320
2826
|
return ScatterResult(
|
|
2321
|
-
vec4<f32>(select(
|
|
2827
|
+
vec4<f32>(select(guidedDirection, guidedLight, useGuidedLight), 0.0),
|
|
2322
2828
|
select(0u, RAY_FLAG_GUIDED_EMISSIVE, useGuidedLight),
|
|
2323
2829
|
0u,
|
|
2324
2830
|
0u,
|
|
@@ -2525,6 +3031,29 @@ fn fragmentMain(in: VertexOut) -> @location(0) vec4<f32> {
|
|
|
2525
3031
|
return textureSample(renderTexture, renderSampler, in.uv);
|
|
2526
3032
|
}
|
|
2527
3033
|
`;
|
|
3034
|
+
function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
3035
|
+
const requiredLimits = { ...options.requiredLimits ?? {} };
|
|
3036
|
+
const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
|
|
3037
|
+
if (Number.isFinite(exposedStorageBufferLimit)) {
|
|
3038
|
+
if (exposedStorageBufferLimit < TRACE_STORAGE_BUFFER_BINDINGS) {
|
|
3039
|
+
throw new Error(
|
|
3040
|
+
`Wavefront mesh tracing requires maxStorageBuffersPerShaderStage>=${TRACE_STORAGE_BUFFER_BINDINGS}, but this adapter exposes ${exposedStorageBufferLimit}.`
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
3043
|
+
requiredLimits.maxStorageBuffersPerShaderStage = Math.max(
|
|
3044
|
+
Number(requiredLimits.maxStorageBuffersPerShaderStage ?? 0),
|
|
3045
|
+
TRACE_STORAGE_BUFFER_BINDINGS
|
|
3046
|
+
);
|
|
3047
|
+
}
|
|
3048
|
+
const descriptor = { ...options.deviceDescriptor ?? {} };
|
|
3049
|
+
if (Object.keys(requiredLimits).length > 0) {
|
|
3050
|
+
descriptor.requiredLimits = {
|
|
3051
|
+
...descriptor.requiredLimits ?? {},
|
|
3052
|
+
...requiredLimits
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
return Object.keys(descriptor).length > 0 ? descriptor : void 0;
|
|
3056
|
+
}
|
|
2528
3057
|
async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
2529
3058
|
assertAnalyticDisplayQualityPolicy(options);
|
|
2530
3059
|
const constants = getGpuUsageConstants();
|
|
@@ -2543,7 +3072,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2543
3072
|
if (!adapter) {
|
|
2544
3073
|
throw new Error("Unable to acquire a WebGPU adapter for wavefront path tracing.");
|
|
2545
3074
|
}
|
|
2546
|
-
const device = await adapter.requestDevice();
|
|
3075
|
+
const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
|
|
2547
3076
|
const context = canvas.getContext("webgpu");
|
|
2548
3077
|
if (!context || typeof context.configure !== "function") {
|
|
2549
3078
|
throw new Error("Canvas WebGPU context does not support configure().");
|
|
@@ -2616,23 +3145,34 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2616
3145
|
Math.max(1, config.gpuMeshSource.meshes.count) * MESH_RANGE_RECORD_BYTES,
|
|
2617
3146
|
"plasius.wavefront.meshRanges"
|
|
2618
3147
|
);
|
|
3148
|
+
const environmentPortalBuffer = createBuffer(
|
|
3149
|
+
device,
|
|
3150
|
+
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
3151
|
+
Math.max(1, config.environmentPortalCapacity) * ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
3152
|
+
"plasius.wavefront.environmentPortals"
|
|
3153
|
+
);
|
|
2619
3154
|
const bvhLeafRefBuffer = createBuffer(
|
|
2620
3155
|
device,
|
|
2621
3156
|
constants.buffer.STORAGE | constants.buffer.COPY_DST,
|
|
2622
3157
|
Math.max(1, config.bvhLeafSortCapacity) * BVH_LEAF_REF_RECORD_BYTES,
|
|
2623
3158
|
"plasius.wavefront.bvhLeafRefs"
|
|
2624
3159
|
);
|
|
2625
|
-
const
|
|
2626
|
-
device,
|
|
2627
|
-
constants.buffer.UNIFORM | constants.buffer.COPY_DST,
|
|
2628
|
-
CONFIG_BUFFER_BYTES,
|
|
2629
|
-
"plasius.wavefront.frameConfig"
|
|
2630
|
-
);
|
|
3160
|
+
const tiles = createTiles(config.width, config.height, config.tileSize);
|
|
2631
3161
|
const uniformOffsetAlignment = Number(device?.limits?.minUniformBufferOffsetAlignment);
|
|
2632
3162
|
const configBufferStride = alignTo(
|
|
2633
3163
|
CONFIG_BUFFER_BYTES,
|
|
2634
3164
|
Number.isFinite(uniformOffsetAlignment) && uniformOffsetAlignment > 0 ? uniformOffsetAlignment : CONFIG_BUFFER_BYTES
|
|
2635
3165
|
);
|
|
3166
|
+
const frameConfigSlotCount = Math.max(
|
|
3167
|
+
1,
|
|
3168
|
+
tiles.length * config.samplesPerPixel + tiles.length + (config.denoise ? 1 : 0)
|
|
3169
|
+
);
|
|
3170
|
+
const configBuffer = createBuffer(
|
|
3171
|
+
device,
|
|
3172
|
+
constants.buffer.UNIFORM | constants.buffer.COPY_DST,
|
|
3173
|
+
frameConfigSlotCount * configBufferStride,
|
|
3174
|
+
"plasius.wavefront.frameConfig"
|
|
3175
|
+
);
|
|
2636
3176
|
const bvhBuildConfigSlots = 1 + config.bvhSortStages.length + config.bvhBuildLevels.length;
|
|
2637
3177
|
const bvhBuildConfigBuffer = createBuffer(
|
|
2638
3178
|
device,
|
|
@@ -2666,6 +3206,11 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2666
3206
|
device.queue.writeBuffer(meshVertexBuffer, 0, config.gpuMeshSource.vertices.buffer);
|
|
2667
3207
|
device.queue.writeBuffer(meshIndexBuffer, 0, config.gpuMeshSource.indices.buffer);
|
|
2668
3208
|
device.queue.writeBuffer(meshRangeBuffer, 0, config.gpuMeshSource.meshes.buffer);
|
|
3209
|
+
const packedEnvironmentPortals = packEnvironmentPortals(
|
|
3210
|
+
config.environmentPortals,
|
|
3211
|
+
Math.max(1, config.environmentPortalCapacity)
|
|
3212
|
+
);
|
|
3213
|
+
device.queue.writeBuffer(environmentPortalBuffer, 0, packedEnvironmentPortals.buffer);
|
|
2669
3214
|
const radianceTexture = device.createTexture({
|
|
2670
3215
|
label: "plasius.wavefront.radiance",
|
|
2671
3216
|
size: { width: config.width, height: config.height },
|
|
@@ -2717,7 +3262,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2717
3262
|
binding: 16,
|
|
2718
3263
|
visibility: constants.shader.COMPUTE,
|
|
2719
3264
|
storageTexture: { access: "write-only", format: "rgba16float" }
|
|
2720
|
-
}
|
|
3265
|
+
},
|
|
3266
|
+
{ binding: 19, visibility: constants.shader.COMPUTE, buffer: { type: "read-only-storage" } }
|
|
2721
3267
|
]
|
|
2722
3268
|
});
|
|
2723
3269
|
const accelerationBindGroupLayout = device.createBindGroupLayout({
|
|
@@ -2890,7 +3436,8 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2890
3436
|
{ binding: 7, resource: outputView },
|
|
2891
3437
|
{ binding: 8, resource: { buffer: triangleBuffer } },
|
|
2892
3438
|
{ binding: 9, resource: { buffer: bvhNodeBuffer } },
|
|
2893
|
-
{ binding: 16, resource: radianceView }
|
|
3439
|
+
{ binding: 16, resource: radianceView },
|
|
3440
|
+
{ binding: 19, resource: { buffer: environmentPortalBuffer } }
|
|
2894
3441
|
]
|
|
2895
3442
|
});
|
|
2896
3443
|
}
|
|
@@ -2977,10 +3524,27 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
2977
3524
|
]
|
|
2978
3525
|
});
|
|
2979
3526
|
let frame = 0;
|
|
2980
|
-
|
|
3527
|
+
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3528
|
+
let accelerationBuildCount = 0;
|
|
3529
|
+
function createFrameConfigWriter(frameIndex) {
|
|
3530
|
+
let slot = 0;
|
|
3531
|
+
return (tile, buildRange = {}) => {
|
|
3532
|
+
if (slot >= frameConfigSlotCount) {
|
|
3533
|
+
throw new Error("Wavefront frame config slot capacity exceeded.");
|
|
3534
|
+
}
|
|
3535
|
+
const offset = slot * configBufferStride;
|
|
3536
|
+
slot += 1;
|
|
3537
|
+
device.queue.writeBuffer(
|
|
3538
|
+
configBuffer,
|
|
3539
|
+
offset,
|
|
3540
|
+
createConfigPayload(config, tile, frameIndex, buildRange)
|
|
3541
|
+
);
|
|
3542
|
+
return offset;
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
2981
3545
|
function dispatchGpuAccelerationBuild(frameIndex) {
|
|
2982
|
-
if (!config.gpuAccelerationBuildRequired) {
|
|
2983
|
-
return;
|
|
3546
|
+
if (!config.gpuAccelerationBuildRequired || accelerationBuilt) {
|
|
3547
|
+
return false;
|
|
2984
3548
|
}
|
|
2985
3549
|
const buildTile = tiles[0] ?? { x: 0, y: 0, width: 1, height: 1 };
|
|
2986
3550
|
const encoder = device.createCommandEncoder({
|
|
@@ -3038,27 +3602,21 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3038
3602
|
}
|
|
3039
3603
|
passEncoder.end();
|
|
3040
3604
|
device.queue.submit([encoder.finish()]);
|
|
3605
|
+
accelerationBuilt = true;
|
|
3606
|
+
accelerationBuildCount += 1;
|
|
3607
|
+
return true;
|
|
3041
3608
|
}
|
|
3042
|
-
function
|
|
3043
|
-
const sampleWeight = 1 / config.samplesPerPixel;
|
|
3044
|
-
const configPayload = createConfigPayload(config, tile, frameIndex, {
|
|
3045
|
-
sampleIndex,
|
|
3046
|
-
sampleWeight
|
|
3047
|
-
});
|
|
3048
|
-
device.queue.writeBuffer(configBuffer, 0, configPayload);
|
|
3049
|
-
const encoder = device.createCommandEncoder({
|
|
3050
|
-
label: `plasius.wavefront.frame.${frameIndex}.tile.${tile.x}.${tile.y}.sample.${sampleIndex}`
|
|
3051
|
-
});
|
|
3609
|
+
function encodeTileSample(encoder, tile, configOffset) {
|
|
3052
3610
|
const passEncoder = encoder.beginComputePass({
|
|
3053
3611
|
label: "plasius.wavefront.computePass"
|
|
3054
3612
|
});
|
|
3055
3613
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
3056
3614
|
const capacityWorkgroups = Math.ceil(config.tilePixelCapacity / WORKGROUP_SIZE);
|
|
3057
|
-
passEncoder.setBindGroup(0, bindGroups[0], [
|
|
3615
|
+
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3058
3616
|
passEncoder.setPipeline(pipelines.generatePrimaryRays);
|
|
3059
3617
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
3060
3618
|
for (let bounceIndex = 0; bounceIndex < config.maxDepth; bounceIndex += 1) {
|
|
3061
|
-
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [
|
|
3619
|
+
passEncoder.setBindGroup(0, bindGroups[bounceIndex % 2], [configOffset]);
|
|
3062
3620
|
passEncoder.setPipeline(pipelines.intersectActiveQueue);
|
|
3063
3621
|
passEncoder.dispatchWorkgroups(capacityWorkgroups);
|
|
3064
3622
|
passEncoder.setPipeline(pipelines.resolveSurfaceRecords);
|
|
@@ -3067,71 +3625,38 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3067
3625
|
passEncoder.dispatchWorkgroups(1);
|
|
3068
3626
|
}
|
|
3069
3627
|
passEncoder.end();
|
|
3070
|
-
device.queue.submit([encoder.finish()]);
|
|
3071
3628
|
}
|
|
3072
|
-
function
|
|
3073
|
-
const configPayload = createConfigPayload(config, tile, frameIndex, {
|
|
3074
|
-
sampleIndex: 0,
|
|
3075
|
-
sampleWeight: 1 / config.samplesPerPixel
|
|
3076
|
-
});
|
|
3077
|
-
device.queue.writeBuffer(configBuffer, 0, configPayload);
|
|
3078
|
-
const encoder = device.createCommandEncoder({
|
|
3079
|
-
label: `plasius.wavefront.frame.${frameIndex}.tile.${tile.x}.${tile.y}.output`
|
|
3080
|
-
});
|
|
3629
|
+
function encodeTileOutput(encoder, tile, configOffset) {
|
|
3081
3630
|
const passEncoder = encoder.beginComputePass({
|
|
3082
3631
|
label: "plasius.wavefront.outputPass"
|
|
3083
3632
|
});
|
|
3084
3633
|
const tileWorkgroups = Math.ceil(tile.width * tile.height / WORKGROUP_SIZE);
|
|
3085
|
-
passEncoder.setBindGroup(0, bindGroups[0], [
|
|
3634
|
+
passEncoder.setBindGroup(0, bindGroups[0], [configOffset]);
|
|
3086
3635
|
passEncoder.setPipeline(pipelines.accumulateTerminalRadiance);
|
|
3087
3636
|
passEncoder.dispatchWorkgroups(tileWorkgroups);
|
|
3088
3637
|
passEncoder.end();
|
|
3089
|
-
device.queue.submit([encoder.finish()]);
|
|
3090
|
-
}
|
|
3091
|
-
function dispatchTile(tile, frameIndex) {
|
|
3092
|
-
for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
|
|
3093
|
-
dispatchTileSample(tile, frameIndex, sampleIndex);
|
|
3094
|
-
}
|
|
3095
|
-
dispatchTileOutput(tile, frameIndex);
|
|
3096
3638
|
}
|
|
3097
|
-
function
|
|
3639
|
+
function encodeDenoise(encoder, configOffset) {
|
|
3098
3640
|
if (!config.denoise) {
|
|
3099
3641
|
return;
|
|
3100
3642
|
}
|
|
3101
|
-
device.queue.writeBuffer(
|
|
3102
|
-
configBuffer,
|
|
3103
|
-
0,
|
|
3104
|
-
createConfigPayload(
|
|
3105
|
-
config,
|
|
3106
|
-
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3107
|
-
frameIndex,
|
|
3108
|
-
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3109
|
-
)
|
|
3110
|
-
);
|
|
3111
|
-
const encoder = device.createCommandEncoder({
|
|
3112
|
-
label: `plasius.wavefront.frame.${frameIndex}.denoise`
|
|
3113
|
-
});
|
|
3114
3643
|
const radiancePass = encoder.beginComputePass({
|
|
3115
3644
|
label: "plasius.wavefront.denoiseRadiancePass"
|
|
3116
3645
|
});
|
|
3117
|
-
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [
|
|
3646
|
+
radiancePass.setBindGroup(0, denoiseRadianceBindGroup, [configOffset]);
|
|
3118
3647
|
radiancePass.setPipeline(pipelines.denoiseLinearRadiance);
|
|
3119
3648
|
radiancePass.dispatchWorkgroups(Math.ceil(config.width / 8), Math.ceil(config.height / 8));
|
|
3120
3649
|
radiancePass.end();
|
|
3121
3650
|
const resolvePass = encoder.beginComputePass({
|
|
3122
3651
|
label: "plasius.wavefront.denoiseResolvePass"
|
|
3123
3652
|
});
|
|
3124
|
-
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [
|
|
3653
|
+
resolvePass.setBindGroup(0, denoiseResolveBindGroup, [configOffset]);
|
|
3125
3654
|
resolvePass.setPipeline(pipelines.resolveDenoisedOutputImage);
|
|
3126
3655
|
resolvePass.dispatchWorkgroups(Math.ceil(config.width / 8), Math.ceil(config.height / 8));
|
|
3127
3656
|
resolvePass.end();
|
|
3128
|
-
device.queue.submit([encoder.finish()]);
|
|
3129
3657
|
}
|
|
3130
|
-
function
|
|
3658
|
+
function encodePresent(encoder) {
|
|
3131
3659
|
const texture = context.getCurrentTexture();
|
|
3132
|
-
const encoder = device.createCommandEncoder({
|
|
3133
|
-
label: `plasius.wavefront.present.${frame}`
|
|
3134
|
-
});
|
|
3135
3660
|
const passEncoder = encoder.beginRenderPass({
|
|
3136
3661
|
label: "plasius.wavefront.presentPass",
|
|
3137
3662
|
colorAttachments: [
|
|
@@ -3147,16 +3672,42 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3147
3672
|
passEncoder.setBindGroup(0, presentBindGroup);
|
|
3148
3673
|
passEncoder.draw(3);
|
|
3149
3674
|
passEncoder.end();
|
|
3675
|
+
}
|
|
3676
|
+
function dispatchFrame(frameIndex) {
|
|
3677
|
+
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3678
|
+
const encoder = device.createCommandEncoder({
|
|
3679
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched`
|
|
3680
|
+
});
|
|
3681
|
+
for (const tile of tiles) {
|
|
3682
|
+
for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
|
|
3683
|
+
const configOffset = writeFrameConfig(tile, {
|
|
3684
|
+
sampleIndex,
|
|
3685
|
+
sampleWeight: 1 / config.samplesPerPixel
|
|
3686
|
+
});
|
|
3687
|
+
encodeTileSample(encoder, tile, configOffset);
|
|
3688
|
+
}
|
|
3689
|
+
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3690
|
+
sampleIndex: 0,
|
|
3691
|
+
sampleWeight: 1 / config.samplesPerPixel
|
|
3692
|
+
});
|
|
3693
|
+
encodeTileOutput(encoder, tile, outputConfigOffset);
|
|
3694
|
+
}
|
|
3695
|
+
if (config.denoise) {
|
|
3696
|
+
const denoiseConfigOffset = writeFrameConfig(
|
|
3697
|
+
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3698
|
+
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3699
|
+
);
|
|
3700
|
+
encodeDenoise(encoder, denoiseConfigOffset);
|
|
3701
|
+
}
|
|
3702
|
+
encodePresent(encoder);
|
|
3150
3703
|
device.queue.submit([encoder.finish()]);
|
|
3704
|
+
return 1;
|
|
3151
3705
|
}
|
|
3152
3706
|
function renderOnce() {
|
|
3153
3707
|
frame += 1;
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
}
|
|
3158
|
-
dispatchDenoise(frame + config.frameIndex);
|
|
3159
|
-
present();
|
|
3708
|
+
const frameIndex = frame + config.frameIndex;
|
|
3709
|
+
const accelerationBuildSubmitted = dispatchGpuAccelerationBuild(frameIndex);
|
|
3710
|
+
const frameSubmissionCount = dispatchFrame(frameIndex);
|
|
3160
3711
|
return Object.freeze({
|
|
3161
3712
|
frame,
|
|
3162
3713
|
width: config.width,
|
|
@@ -3170,10 +3721,17 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3170
3721
|
sceneObjectCount: config.sceneObjectCount,
|
|
3171
3722
|
triangleCount: config.triangleCount,
|
|
3172
3723
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
3724
|
+
environmentPortalCount: config.environmentPortalCount,
|
|
3725
|
+
environmentPortalMode: config.environmentPortalMode,
|
|
3173
3726
|
bvhNodeCount: config.bvhNodeCount,
|
|
3174
3727
|
displayQuality: config.displayQuality,
|
|
3175
3728
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
3176
3729
|
gpuAccelerationBuildRequired: config.gpuAccelerationBuildRequired,
|
|
3730
|
+
accelerationBuildSubmitted,
|
|
3731
|
+
accelerationBuilt,
|
|
3732
|
+
accelerationBuildCount,
|
|
3733
|
+
commandSubmissions: frameSubmissionCount + (accelerationBuildSubmitted ? 1 : 0),
|
|
3734
|
+
frameConfigSlots: frameConfigSlotCount,
|
|
3177
3735
|
memory: config.memory
|
|
3178
3736
|
});
|
|
3179
3737
|
}
|
|
@@ -3239,10 +3797,15 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3239
3797
|
sceneObjectCount: config.sceneObjectCount,
|
|
3240
3798
|
triangleCount: config.triangleCount,
|
|
3241
3799
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
3800
|
+
environmentPortalCount: config.environmentPortalCount,
|
|
3801
|
+
environmentPortalMode: config.environmentPortalMode,
|
|
3242
3802
|
bvhNodeCount: config.bvhNodeCount,
|
|
3243
3803
|
displayQuality: config.displayQuality,
|
|
3244
3804
|
accelerationBuildMode: config.accelerationBuildMode,
|
|
3245
3805
|
gpuAccelerationBuildRequired: config.gpuAccelerationBuildRequired,
|
|
3806
|
+
accelerationBuilt,
|
|
3807
|
+
accelerationBuildCount,
|
|
3808
|
+
frameConfigSlots: frameConfigSlotCount,
|
|
3246
3809
|
memory: config.memory
|
|
3247
3810
|
});
|
|
3248
3811
|
}
|
|
@@ -3257,6 +3820,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3257
3820
|
meshVertexBuffer.destroy?.();
|
|
3258
3821
|
meshIndexBuffer.destroy?.();
|
|
3259
3822
|
meshRangeBuffer.destroy?.();
|
|
3823
|
+
environmentPortalBuffer.destroy?.();
|
|
3260
3824
|
bvhLeafRefBuffer.destroy?.();
|
|
3261
3825
|
configBuffer.destroy?.();
|
|
3262
3826
|
bvhBuildConfigBuffer.destroy?.();
|
|
@@ -4580,11 +5144,13 @@ var defaultRendererClearColor = DEFAULT_CLEAR_COLOR;
|
|
|
4580
5144
|
createWavefrontPathTracingComputeConfig,
|
|
4581
5145
|
createWavefrontPathTracingComputeRenderer,
|
|
4582
5146
|
createWavefrontPathTracingPlan,
|
|
5147
|
+
createWavefrontReferenceRay,
|
|
4583
5148
|
defaultRendererClearColor,
|
|
4584
5149
|
defaultRendererWorkerProfile,
|
|
4585
5150
|
estimateWavefrontPathTracingMemory,
|
|
4586
5151
|
getRendererWorkerManifest,
|
|
4587
5152
|
getRendererWorkerProfile,
|
|
5153
|
+
intersectWavefrontReferenceTriangle,
|
|
4588
5154
|
normalizeWavefrontMesh,
|
|
4589
5155
|
normalizeWavefrontSceneObject,
|
|
4590
5156
|
packWavefrontBvhNodes,
|
|
@@ -4604,6 +5170,7 @@ var defaultRendererClearColor = DEFAULT_CLEAR_COLOR;
|
|
|
4604
5170
|
rendererWorkerQueueClass,
|
|
4605
5171
|
supportsWavefrontPathTracingCompute,
|
|
4606
5172
|
supportsWebGpu,
|
|
5173
|
+
traceWavefrontReferenceTriangles,
|
|
4607
5174
|
wavefrontMaterialKinds,
|
|
4608
5175
|
wavefrontPathTracingComputeLimits,
|
|
4609
5176
|
wavefrontSceneObjectKinds
|