@plasius/gpu-lighting 0.2.5 → 0.2.6
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 +14 -3
- package/README.md +37 -0
- package/dist/index.cjs +511 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +499 -0
- package/dist/index.js.map +1 -1
- package/dist/techniques/techniques/wavefront/accumulate-terminal-radiance.job.wgsl +61 -0
- package/dist/techniques/techniques/wavefront/prelude.wgsl +229 -0
- package/dist/techniques/techniques/wavefront/scatter-continuations.job.wgsl +136 -0
- package/package.json +2 -1
- package/src/index.js +535 -0
- package/src/techniques/wavefront/accumulate-terminal-radiance.job.wgsl +61 -0
- package/src/techniques/wavefront/prelude.wgsl +229 -0
- package/src/techniques/wavefront/scatter-continuations.job.wgsl +136 -0
package/CHANGELOG.md
CHANGED
|
@@ -20,7 +20,7 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
20
20
|
- **Security**
|
|
21
21
|
- (placeholder)
|
|
22
22
|
|
|
23
|
-
## [0.2.
|
|
23
|
+
## [0.2.6] - 2026-06-16
|
|
24
24
|
|
|
25
25
|
- **Added**
|
|
26
26
|
- Added concrete volumetric WGSL kernels for `volumetricShadow` and
|
|
@@ -28,14 +28,25 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
28
28
|
integration for the published realtime and reference profiles.
|
|
29
29
|
- Added concrete HDRI/IBL WGSL kernels for `irradianceConvolution`,
|
|
30
30
|
`specularPrefilter`, and `brdfLut`.
|
|
31
|
+
- Added a renderer-aligned `wavefront` lighting technique with concrete
|
|
32
|
+
WGSL jobs for terminal radiance accumulation and continuation scattering.
|
|
33
|
+
- Added `createWavefrontLightingPlan()`,
|
|
34
|
+
`evaluateWavefrontTerminalRadiance()`, and
|
|
35
|
+
`evaluateWavefrontContinuationEvent()` so downstream packages and tests can
|
|
36
|
+
validate emissive-hit, environment-hit, miss-darkening, reflection,
|
|
37
|
+
refraction, and transparency behavior without reimplementing the contract.
|
|
31
38
|
|
|
32
39
|
- **Changed**
|
|
33
40
|
- README now documents the delivered volumetrics and HDRI kernel scope with
|
|
34
41
|
technique-level descriptions instead of leaving those exported jobs implied.
|
|
42
|
+
- Eames capture helpers now resolve workspace roots correctly from both
|
|
43
|
+
ordinary repo checkouts and `git worktree` paths.
|
|
35
44
|
|
|
36
45
|
- **Fixed**
|
|
37
46
|
- Package tests now fail if any exported `hybrid`, `volumetrics`, or `hdri`
|
|
38
47
|
job regresses to placeholder text or an empty/no-op `process_job` body.
|
|
48
|
+
- Eames asset-path and capture-bridge tests are now worktree-safe instead of
|
|
49
|
+
assuming the package always lives under a literal `/gpu-lighting/` path.
|
|
39
50
|
|
|
40
51
|
## [0.2.2] - 2026-06-11
|
|
41
52
|
|
|
@@ -464,7 +475,7 @@ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/
|
|
|
464
475
|
[0.1.16]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.16
|
|
465
476
|
[0.1.17]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.17
|
|
466
477
|
[0.1.19]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.1.19
|
|
467
|
-
[Unreleased]: https://github.com/Plasius-LTD/gpu-lighting/compare/v0.2.
|
|
478
|
+
[Unreleased]: https://github.com/Plasius-LTD/gpu-lighting/compare/v0.2.6...HEAD
|
|
468
479
|
[0.2.0]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.0
|
|
469
480
|
[0.2.2]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.2
|
|
470
|
-
[0.2.
|
|
481
|
+
[0.2.6]: https://github.com/Plasius-LTD/gpu-lighting/releases/tag/v0.2.6
|
package/README.md
CHANGED
|
@@ -162,6 +162,43 @@ const profileManifest = getLightingProfileWorkerManifest("realtime");
|
|
|
162
162
|
console.log(profileManifest.jobs.map((job) => job.worker.jobType));
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
## Wavefront Lighting Contracts
|
|
166
|
+
|
|
167
|
+
`@plasius/gpu-lighting` now also publishes a renderer-aligned `wavefront`
|
|
168
|
+
technique for the first active-ray lighting slice. It keeps the queue layout,
|
|
169
|
+
buffer contract names, and terminal-hit policy aligned with
|
|
170
|
+
`@plasius/gpu-renderer` while owning the lighting-specific WGSL for terminal
|
|
171
|
+
radiance accumulation and continuation scattering.
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
import {
|
|
175
|
+
createWavefrontLightingPlan,
|
|
176
|
+
evaluateWavefrontTerminalRadiance,
|
|
177
|
+
loadLightingTechniqueWorkerBundle,
|
|
178
|
+
} from "@plasius/gpu-lighting";
|
|
179
|
+
|
|
180
|
+
const plan = createWavefrontLightingPlan({
|
|
181
|
+
maxDepth: 6,
|
|
182
|
+
queueCapacity: 4096,
|
|
183
|
+
explicitLightSampling: true,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const bundle = await loadLightingTechniqueWorkerBundle("wavefront");
|
|
187
|
+
const emissive = evaluateWavefrontTerminalRadiance({
|
|
188
|
+
hitType: "emissive",
|
|
189
|
+
throughput: [0.5, 0.5, 0.5],
|
|
190
|
+
emission: [8, 6, 4],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
console.log(plan.requiredRendererPassOrder);
|
|
194
|
+
console.log(bundle.jobs.map((job) => job.label));
|
|
195
|
+
console.log(emissive.radiance);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This slice keeps emissive hits, environment hits, and environment-miss dark
|
|
199
|
+
fallbacks on the lighting package surface without reintroducing a depth-first
|
|
200
|
+
shader dependency into the renderer-owned wavefront queue model.
|
|
201
|
+
|
|
165
202
|
## Distance-Banded Lighting
|
|
166
203
|
|
|
167
204
|
```js
|
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,12 @@ __export(index_exports, {
|
|
|
33
33
|
createLightingBandPlan: () => createLightingBandPlan,
|
|
34
34
|
createLightingProfileModeLadder: () => createLightingProfileModeLadder,
|
|
35
35
|
createWavefrontEnvironmentLightingOptions: () => createWavefrontEnvironmentLightingOptions,
|
|
36
|
+
createWavefrontLightingPlan: () => createWavefrontLightingPlan,
|
|
36
37
|
defaultAdaptiveLightingProfilePolicy: () => defaultAdaptiveLightingProfilePolicy,
|
|
37
38
|
defaultLightingProfile: () => defaultLightingProfile,
|
|
38
39
|
defaultLightingTechnique: () => defaultLightingTechnique,
|
|
40
|
+
evaluateWavefrontContinuationEvent: () => evaluateWavefrontContinuationEvent,
|
|
41
|
+
evaluateWavefrontTerminalRadiance: () => evaluateWavefrontTerminalRadiance,
|
|
39
42
|
getLightingProfile: () => getLightingProfile,
|
|
40
43
|
getLightingProfileWorkerManifest: () => getLightingProfileWorkerManifest,
|
|
41
44
|
getLightingTechnique: () => getLightingTechnique,
|
|
@@ -54,8 +57,17 @@ __export(index_exports, {
|
|
|
54
57
|
lightingProfileModeOrder: () => lightingProfileModeOrder,
|
|
55
58
|
lightingProfileNames: () => lightingProfileNames,
|
|
56
59
|
lightingProfiles: () => lightingProfiles,
|
|
60
|
+
lightingRequiredRendererWavefrontPassOrder: () => lightingRequiredRendererWavefrontPassOrder,
|
|
57
61
|
lightingTechniqueNames: () => lightingTechniqueNames,
|
|
58
62
|
lightingTechniques: () => lightingTechniques,
|
|
63
|
+
lightingWavefrontBufferContracts: () => lightingWavefrontBufferContracts,
|
|
64
|
+
lightingWavefrontContinuationHitTypes: () => lightingWavefrontContinuationHitTypes,
|
|
65
|
+
lightingWavefrontHitTypes: () => lightingWavefrontHitTypes,
|
|
66
|
+
lightingWavefrontPassOrder: () => lightingWavefrontPassOrder,
|
|
67
|
+
lightingWavefrontQueuePairStrategy: () => lightingWavefrontQueuePairStrategy,
|
|
68
|
+
lightingWavefrontSchemaVersion: () => lightingWavefrontSchemaVersion,
|
|
69
|
+
lightingWavefrontTerminalHitTypes: () => lightingWavefrontTerminalHitTypes,
|
|
70
|
+
lightingWavefrontTerminationPolicy: () => lightingWavefrontTerminationPolicy,
|
|
59
71
|
lightingWorkerManifests: () => lightingWorkerManifests,
|
|
60
72
|
lightingWorkerQueueClass: () => lightingWorkerQueueClass,
|
|
61
73
|
loadLightingJobs: () => loadLightingJobs,
|
|
@@ -100,6 +112,14 @@ var techniqueSpecs = {
|
|
|
100
112
|
denoise: "denoise.job.wgsl"
|
|
101
113
|
}
|
|
102
114
|
},
|
|
115
|
+
wavefront: {
|
|
116
|
+
description: "Renderer-aligned wavefront lighting jobs for terminal radiance and continuation scattering.",
|
|
117
|
+
prelude: "prelude.wgsl",
|
|
118
|
+
jobs: {
|
|
119
|
+
accumulateTerminalRadiance: "accumulate-terminal-radiance.job.wgsl",
|
|
120
|
+
scatterContinuations: "scatter-continuations.job.wgsl"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
103
123
|
volumetrics: {
|
|
104
124
|
description: "Froxel volumetric lighting for fog, shafts, and participating media shadows.",
|
|
105
125
|
prelude: "prelude.wgsl",
|
|
@@ -1108,6 +1128,374 @@ function createWavefrontEnvironmentLightingOptions(options = {}) {
|
|
|
1108
1128
|
lightingEnvironment: config
|
|
1109
1129
|
});
|
|
1110
1130
|
}
|
|
1131
|
+
var lightingWavefrontSchemaVersion = 1;
|
|
1132
|
+
var lightingWavefrontQueuePairStrategy = "ping-pong-active-next";
|
|
1133
|
+
var lightingWavefrontHitTypes = Object.freeze([
|
|
1134
|
+
"surface",
|
|
1135
|
+
"emissive",
|
|
1136
|
+
"environment",
|
|
1137
|
+
"transparent",
|
|
1138
|
+
"miss"
|
|
1139
|
+
]);
|
|
1140
|
+
var lightingWavefrontTerminalHitTypes = Object.freeze([
|
|
1141
|
+
"emissive",
|
|
1142
|
+
"environment",
|
|
1143
|
+
"miss"
|
|
1144
|
+
]);
|
|
1145
|
+
var lightingWavefrontContinuationHitTypes = Object.freeze([
|
|
1146
|
+
"surface",
|
|
1147
|
+
"transparent"
|
|
1148
|
+
]);
|
|
1149
|
+
var lightingWavefrontPassOrder = Object.freeze([
|
|
1150
|
+
"accumulateTerminalRadiance",
|
|
1151
|
+
"scatterContinuations"
|
|
1152
|
+
]);
|
|
1153
|
+
var lightingRequiredRendererWavefrontPassOrder = Object.freeze([
|
|
1154
|
+
"generatePrimaryRays",
|
|
1155
|
+
"intersectActiveQueue",
|
|
1156
|
+
"resolveSurfaceRecords",
|
|
1157
|
+
"accumulateTerminalRadiance",
|
|
1158
|
+
"scatterContinuations",
|
|
1159
|
+
"compactAndSwapQueues"
|
|
1160
|
+
]);
|
|
1161
|
+
function createLightingWavefrontField(name, type, description) {
|
|
1162
|
+
return Object.freeze({
|
|
1163
|
+
name,
|
|
1164
|
+
type,
|
|
1165
|
+
description
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
function createLightingWavefrontRecordContract(recordName, fields) {
|
|
1169
|
+
return Object.freeze({
|
|
1170
|
+
schemaVersion: lightingWavefrontSchemaVersion,
|
|
1171
|
+
recordName,
|
|
1172
|
+
fields: Object.freeze(fields)
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
var lightingWavefrontBufferContracts = Object.freeze({
|
|
1176
|
+
ray: createLightingWavefrontRecordContract(
|
|
1177
|
+
"RayRecord",
|
|
1178
|
+
[
|
|
1179
|
+
createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
|
|
1180
|
+
createLightingWavefrontField("parentRayId", "u32", "Parent ray identifier."),
|
|
1181
|
+
createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
|
|
1182
|
+
createLightingWavefrontField("sampleId", "u32", "Per-pixel sample slot."),
|
|
1183
|
+
createLightingWavefrontField("bounce", "u32", "Current bounce depth."),
|
|
1184
|
+
createLightingWavefrontField("origin", "vec3<f32>", "Ray origin."),
|
|
1185
|
+
createLightingWavefrontField("direction", "vec3<f32>", "Normalized ray direction."),
|
|
1186
|
+
createLightingWavefrontField("throughput", "vec3<f32>", "Accumulated path throughput."),
|
|
1187
|
+
createLightingWavefrontField("mediumRefId", "u32", "Current medium reference."),
|
|
1188
|
+
createLightingWavefrontField("flags", "u32", "Renderer-owned ray flags.")
|
|
1189
|
+
]
|
|
1190
|
+
),
|
|
1191
|
+
hit: createLightingWavefrontRecordContract(
|
|
1192
|
+
"HitRecord",
|
|
1193
|
+
[
|
|
1194
|
+
createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
|
|
1195
|
+
createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
|
|
1196
|
+
createLightingWavefrontField("hitType", "u32", "Surface, emissive, environment, transparent, or miss."),
|
|
1197
|
+
createLightingWavefrontField("distance", "f32", "Nearest-hit distance."),
|
|
1198
|
+
createLightingWavefrontField("entityId", "u32", "Owning entity identifier."),
|
|
1199
|
+
createLightingWavefrontField("instanceId", "u32", "Owning instance identifier."),
|
|
1200
|
+
createLightingWavefrontField("primitiveId", "u32", "Primitive identifier."),
|
|
1201
|
+
createLightingWavefrontField("materialId", "u32", "Resolved material identifier."),
|
|
1202
|
+
createLightingWavefrontField("barycentrics", "vec3<f32>", "Triangle barycentrics."),
|
|
1203
|
+
createLightingWavefrontField("uv", "vec2<f32>", "Resolved UV coordinates."),
|
|
1204
|
+
createLightingWavefrontField("geometricNormal", "vec3<f32>", "Geometric surface normal."),
|
|
1205
|
+
createLightingWavefrontField("shadingNormal", "vec3<f32>", "Interpolated shading normal."),
|
|
1206
|
+
createLightingWavefrontField("frontFace", "u32", "Front-face classification.")
|
|
1207
|
+
]
|
|
1208
|
+
),
|
|
1209
|
+
surface: createLightingWavefrontRecordContract(
|
|
1210
|
+
"SurfaceRecord",
|
|
1211
|
+
[
|
|
1212
|
+
createLightingWavefrontField("rayId", "u32", "Stable ray identifier."),
|
|
1213
|
+
createLightingWavefrontField("entityId", "u32", "Owning entity identifier."),
|
|
1214
|
+
createLightingWavefrontField("materialRefId", "u32", "Renderer material reference id."),
|
|
1215
|
+
createLightingWavefrontField("mediumRefId", "u32", "Renderer medium reference id."),
|
|
1216
|
+
createLightingWavefrontField("geometricNormal", "vec3<f32>", "Geometric surface normal."),
|
|
1217
|
+
createLightingWavefrontField("shadingNormal", "vec3<f32>", "Interpolated shading normal."),
|
|
1218
|
+
createLightingWavefrontField("uv", "vec2<f32>", "Resolved UV coordinates."),
|
|
1219
|
+
createLightingWavefrontField("tangentFrame", "mat3x3<f32>", "Resolved tangent frame.")
|
|
1220
|
+
]
|
|
1221
|
+
),
|
|
1222
|
+
materialReference: createLightingWavefrontRecordContract(
|
|
1223
|
+
"MaterialReferenceRecord",
|
|
1224
|
+
[
|
|
1225
|
+
createLightingWavefrontField("materialRefId", "u32", "Lighting-visible material reference id."),
|
|
1226
|
+
createLightingWavefrontField("materialId", "u32", "Source material identifier."),
|
|
1227
|
+
createLightingWavefrontField("shadingModel", "u32", "Renderer shading-model discriminator."),
|
|
1228
|
+
createLightingWavefrontField("textureSetId", "u32", "Resolved texture-set identifier."),
|
|
1229
|
+
createLightingWavefrontField("flags", "u32", "Renderer material flags.")
|
|
1230
|
+
]
|
|
1231
|
+
),
|
|
1232
|
+
mediumReference: createLightingWavefrontRecordContract(
|
|
1233
|
+
"MediumReferenceRecord",
|
|
1234
|
+
[
|
|
1235
|
+
createLightingWavefrontField("mediumRefId", "u32", "Lighting-visible medium reference id."),
|
|
1236
|
+
createLightingWavefrontField("mediumId", "u32", "Source medium identifier."),
|
|
1237
|
+
createLightingWavefrontField("phaseModel", "u32", "Phase-function discriminator."),
|
|
1238
|
+
createLightingWavefrontField("absorption", "vec3<f32>", "Medium absorption coefficients."),
|
|
1239
|
+
createLightingWavefrontField("scattering", "vec3<f32>", "Medium scattering coefficients.")
|
|
1240
|
+
]
|
|
1241
|
+
),
|
|
1242
|
+
accumulation: createLightingWavefrontRecordContract(
|
|
1243
|
+
"AccumulationRecord",
|
|
1244
|
+
[
|
|
1245
|
+
createLightingWavefrontField("sourcePixelId", "u32", "Source pixel/sample owner."),
|
|
1246
|
+
createLightingWavefrontField("sampleCount", "u32", "Accumulated sample count."),
|
|
1247
|
+
createLightingWavefrontField("radiance", "vec3<f32>", "Accumulated radiance."),
|
|
1248
|
+
createLightingWavefrontField("throughput", "vec3<f32>", "Last surviving throughput."),
|
|
1249
|
+
createLightingWavefrontField("resetEpoch", "u32", "Renderer accumulation reset epoch.")
|
|
1250
|
+
]
|
|
1251
|
+
)
|
|
1252
|
+
});
|
|
1253
|
+
var lightingWavefrontTerminationPolicy = Object.freeze({
|
|
1254
|
+
terminalHitTypes: lightingWavefrontTerminalHitTypes,
|
|
1255
|
+
continuationHitTypes: lightingWavefrontContinuationHitTypes,
|
|
1256
|
+
emissive: Object.freeze({
|
|
1257
|
+
action: "accumulate-and-stop",
|
|
1258
|
+
contributesRadiance: true
|
|
1259
|
+
}),
|
|
1260
|
+
environment: Object.freeze({
|
|
1261
|
+
action: "accumulate-and-stop",
|
|
1262
|
+
contributesRadiance: true
|
|
1263
|
+
}),
|
|
1264
|
+
miss: Object.freeze({
|
|
1265
|
+
action: "accumulate-environment-or-dark-stop",
|
|
1266
|
+
contributesRadiance: true
|
|
1267
|
+
})
|
|
1268
|
+
});
|
|
1269
|
+
var defaultWavefrontDarkRadiance = Object.freeze([1e-4, 1e-4, 1e-4]);
|
|
1270
|
+
var wavefrontEventKinds = Object.freeze([
|
|
1271
|
+
"diffuse",
|
|
1272
|
+
"reflection",
|
|
1273
|
+
"refraction",
|
|
1274
|
+
"transparency",
|
|
1275
|
+
"terminate"
|
|
1276
|
+
]);
|
|
1277
|
+
function normalizeWavefrontHitType(value) {
|
|
1278
|
+
return lightingWavefrontHitTypes.includes(value) ? value : "surface";
|
|
1279
|
+
}
|
|
1280
|
+
function normalizeWavefrontEventKind(value) {
|
|
1281
|
+
return wavefrontEventKinds.includes(value) ? value : null;
|
|
1282
|
+
}
|
|
1283
|
+
function normalizeVec3(value, fallback = [0, 0, 0]) {
|
|
1284
|
+
if (!Array.isArray(value) || value.length < 3) {
|
|
1285
|
+
return [...fallback];
|
|
1286
|
+
}
|
|
1287
|
+
return [
|
|
1288
|
+
Number.isFinite(value[0]) ? value[0] : fallback[0],
|
|
1289
|
+
Number.isFinite(value[1]) ? value[1] : fallback[1],
|
|
1290
|
+
Number.isFinite(value[2]) ? value[2] : fallback[2]
|
|
1291
|
+
];
|
|
1292
|
+
}
|
|
1293
|
+
function clampUnit(value, fallback = 0) {
|
|
1294
|
+
return Math.max(0, Math.min(1, readFinite(value, fallback)));
|
|
1295
|
+
}
|
|
1296
|
+
function saturateVec3(value) {
|
|
1297
|
+
return value.map((component) => Math.max(0, component));
|
|
1298
|
+
}
|
|
1299
|
+
function scaleVec3(value, scalar) {
|
|
1300
|
+
return value.map((component) => component * scalar);
|
|
1301
|
+
}
|
|
1302
|
+
function addVec3(left, right) {
|
|
1303
|
+
return left.map((component, index) => component + right[index]);
|
|
1304
|
+
}
|
|
1305
|
+
function multiplyVec3(left, right) {
|
|
1306
|
+
return left.map((component, index) => component * right[index]);
|
|
1307
|
+
}
|
|
1308
|
+
function dotVec3(left, right) {
|
|
1309
|
+
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
|
1310
|
+
}
|
|
1311
|
+
function lengthVec3(value) {
|
|
1312
|
+
return Math.hypot(value[0], value[1], value[2]);
|
|
1313
|
+
}
|
|
1314
|
+
function normalizeDirection(value, fallback = [0, 1, 0]) {
|
|
1315
|
+
const vector = normalizeVec3(value, fallback);
|
|
1316
|
+
const length = lengthVec3(vector);
|
|
1317
|
+
if (!Number.isFinite(length) || length <= 1e-6) {
|
|
1318
|
+
return [...fallback];
|
|
1319
|
+
}
|
|
1320
|
+
return vector.map((component) => component / length);
|
|
1321
|
+
}
|
|
1322
|
+
function mixVec3(left, right, factor) {
|
|
1323
|
+
return left.map(
|
|
1324
|
+
(component, index) => component * (1 - factor) + right[index] * factor
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
function reflectDirection(direction, normal) {
|
|
1328
|
+
const scale = 2 * dotVec3(direction, normal);
|
|
1329
|
+
return normalizeDirection(
|
|
1330
|
+
[
|
|
1331
|
+
direction[0] - scale * normal[0],
|
|
1332
|
+
direction[1] - scale * normal[1],
|
|
1333
|
+
direction[2] - scale * normal[2]
|
|
1334
|
+
],
|
|
1335
|
+
normal
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
function refractDirection(direction, normal, etaRatio) {
|
|
1339
|
+
const cosTheta = Math.min(-dotVec3(direction, normal), 1);
|
|
1340
|
+
const rOutPerp = scaleVec3(addVec3(direction, scaleVec3(normal, cosTheta)), etaRatio);
|
|
1341
|
+
const rOutPerpLengthSquared = dotVec3(rOutPerp, rOutPerp);
|
|
1342
|
+
const parallelFactor = 1 - rOutPerpLengthSquared;
|
|
1343
|
+
if (parallelFactor <= 0) {
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
const rOutParallel = scaleVec3(normal, -Math.sqrt(parallelFactor));
|
|
1347
|
+
return normalizeDirection(addVec3(rOutPerp, rOutParallel), direction);
|
|
1348
|
+
}
|
|
1349
|
+
function createWavefrontLightingPlan(options = {}) {
|
|
1350
|
+
const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
|
|
1351
|
+
const queueCapacity = Math.max(
|
|
1352
|
+
1,
|
|
1353
|
+
Math.trunc(readFinite(options.queueCapacity, 4096))
|
|
1354
|
+
);
|
|
1355
|
+
const explicitLightSampling = Boolean(options.explicitLightSampling);
|
|
1356
|
+
const accumulationResetEpoch = Math.max(
|
|
1357
|
+
0,
|
|
1358
|
+
Math.trunc(readFinite(options.accumulationResetEpoch, 0))
|
|
1359
|
+
);
|
|
1360
|
+
return Object.freeze({
|
|
1361
|
+
schemaVersion: lightingWavefrontSchemaVersion,
|
|
1362
|
+
maxDepth,
|
|
1363
|
+
queueCapacity,
|
|
1364
|
+
explicitLightSampling,
|
|
1365
|
+
accumulationResetEpoch,
|
|
1366
|
+
queueLayout: Object.freeze({
|
|
1367
|
+
strategy: lightingWavefrontQueuePairStrategy,
|
|
1368
|
+
compactAfterScatter: true,
|
|
1369
|
+
queues: Object.freeze([
|
|
1370
|
+
Object.freeze({ name: "active", role: "current-bounce" }),
|
|
1371
|
+
Object.freeze({ name: "next", role: "next-bounce" })
|
|
1372
|
+
])
|
|
1373
|
+
}),
|
|
1374
|
+
bufferContracts: lightingWavefrontBufferContracts,
|
|
1375
|
+
terminationPolicy: lightingWavefrontTerminationPolicy,
|
|
1376
|
+
requiredRendererPassOrder: lightingRequiredRendererWavefrontPassOrder,
|
|
1377
|
+
lightingPasses: Object.freeze([
|
|
1378
|
+
Object.freeze({
|
|
1379
|
+
key: "accumulateTerminalRadiance",
|
|
1380
|
+
stage: "accumulateTerminalRadiance",
|
|
1381
|
+
reads: Object.freeze([
|
|
1382
|
+
"ray",
|
|
1383
|
+
"hit",
|
|
1384
|
+
"surface",
|
|
1385
|
+
"materialReference",
|
|
1386
|
+
"mediumReference"
|
|
1387
|
+
]),
|
|
1388
|
+
writes: Object.freeze(["accumulation"]),
|
|
1389
|
+
terminalHitTypes: lightingWavefrontTerminalHitTypes
|
|
1390
|
+
}),
|
|
1391
|
+
Object.freeze({
|
|
1392
|
+
key: "scatterContinuations",
|
|
1393
|
+
stage: "scatterContinuations",
|
|
1394
|
+
reads: Object.freeze([
|
|
1395
|
+
"ray",
|
|
1396
|
+
"hit",
|
|
1397
|
+
"surface",
|
|
1398
|
+
"materialReference",
|
|
1399
|
+
"mediumReference"
|
|
1400
|
+
]),
|
|
1401
|
+
writes: Object.freeze(["ray"]),
|
|
1402
|
+
continuationHitTypes: lightingWavefrontContinuationHitTypes,
|
|
1403
|
+
explicitLightSampling
|
|
1404
|
+
})
|
|
1405
|
+
])
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
function evaluateWavefrontTerminalRadiance(options = {}) {
|
|
1409
|
+
const hitType = normalizeWavefrontHitType(options.hitType);
|
|
1410
|
+
const throughput = saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]));
|
|
1411
|
+
const emission = saturateVec3(normalizeVec3(options.emission, [0, 0, 0]));
|
|
1412
|
+
const environmentRadiance = saturateVec3(
|
|
1413
|
+
normalizeVec3(options.environmentRadiance, [0, 0, 0])
|
|
1414
|
+
);
|
|
1415
|
+
const missRadiance = saturateVec3(
|
|
1416
|
+
normalizeVec3(options.missRadiance, defaultWavefrontDarkRadiance)
|
|
1417
|
+
);
|
|
1418
|
+
const environmentLuminance = colorLuminance(environmentRadiance);
|
|
1419
|
+
let source = "none";
|
|
1420
|
+
let rawRadiance = [0, 0, 0];
|
|
1421
|
+
if (hitType === "emissive") {
|
|
1422
|
+
source = "emissive";
|
|
1423
|
+
rawRadiance = emission;
|
|
1424
|
+
} else if (hitType === "environment") {
|
|
1425
|
+
source = "environment";
|
|
1426
|
+
rawRadiance = environmentRadiance;
|
|
1427
|
+
} else if (hitType === "miss") {
|
|
1428
|
+
source = environmentLuminance > 1e-6 ? "environment" : "dark";
|
|
1429
|
+
rawRadiance = environmentLuminance > 1e-6 ? environmentRadiance : missRadiance;
|
|
1430
|
+
}
|
|
1431
|
+
const radiance = multiplyVec3(throughput, rawRadiance);
|
|
1432
|
+
const terminated = lightingWavefrontTerminalHitTypes.includes(hitType);
|
|
1433
|
+
return Object.freeze({
|
|
1434
|
+
hitType,
|
|
1435
|
+
source,
|
|
1436
|
+
terminated,
|
|
1437
|
+
radiance: Object.freeze(radiance),
|
|
1438
|
+
nearDarkSample: source === "dark" && colorLuminance(radiance) <= colorLuminance(defaultWavefrontDarkRadiance)
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
function evaluateWavefrontContinuationEvent(options = {}) {
|
|
1442
|
+
const hitType = normalizeWavefrontHitType(options.hitType);
|
|
1443
|
+
const bounceIndex = Math.max(0, Math.trunc(readFinite(options.bounceIndex, 0)));
|
|
1444
|
+
const maxDepth = Math.max(1, Math.trunc(readFinite(options.maxDepth, 4)));
|
|
1445
|
+
const throughput = saturateVec3(normalizeVec3(options.throughput, [1, 1, 1]));
|
|
1446
|
+
const albedo = saturateVec3(normalizeVec3(options.albedo, [0.8, 0.8, 0.8]));
|
|
1447
|
+
const transmission = saturateVec3(
|
|
1448
|
+
normalizeVec3(options.transmission, [0, 0, 0])
|
|
1449
|
+
);
|
|
1450
|
+
const shadingNormal = normalizeDirection(options.shadingNormal, [0, 1, 0]);
|
|
1451
|
+
const viewDirection = normalizeDirection(options.viewDirection, [0, 0, 1]);
|
|
1452
|
+
const incomingDirection = normalizeDirection(
|
|
1453
|
+
scaleVec3(viewDirection, -1),
|
|
1454
|
+
[0, 0, -1]
|
|
1455
|
+
);
|
|
1456
|
+
const frontFace = options.frontFace !== false;
|
|
1457
|
+
const orientedNormal = frontFace ? shadingNormal : scaleVec3(shadingNormal, -1);
|
|
1458
|
+
const metalness = clampUnit(options.metalness, 0);
|
|
1459
|
+
const roughness = clampUnit(options.roughness, 0.5);
|
|
1460
|
+
const opacity = clampUnit(options.opacity, 1);
|
|
1461
|
+
const refractiveIndex = Math.max(1, readFinite(options.refractiveIndex ?? options.ior, 1.45));
|
|
1462
|
+
const transmissionStrength = Math.max(...transmission);
|
|
1463
|
+
let eventKind = normalizeWavefrontEventKind(options.eventKind) ?? (hitType === "transparent" || opacity < 0.999 ? "transparency" : transmissionStrength > 1e-3 ? "refraction" : metalness >= 0.5 || roughness <= 0.2 ? "reflection" : "diffuse");
|
|
1464
|
+
let nextDirection = incomingDirection;
|
|
1465
|
+
let attenuation;
|
|
1466
|
+
if (!lightingWavefrontContinuationHitTypes.includes(hitType) || bounceIndex >= maxDepth - 1) {
|
|
1467
|
+
eventKind = "terminate";
|
|
1468
|
+
attenuation = [0, 0, 0];
|
|
1469
|
+
} else if (eventKind === "reflection") {
|
|
1470
|
+
nextDirection = reflectDirection(incomingDirection, orientedNormal);
|
|
1471
|
+
attenuation = mixVec3([0.04, 0.04, 0.04], albedo, metalness);
|
|
1472
|
+
} else if (eventKind === "refraction") {
|
|
1473
|
+
const etaRatio = frontFace ? 1 / refractiveIndex : refractiveIndex;
|
|
1474
|
+
nextDirection = refractDirection(incomingDirection, orientedNormal, etaRatio) ?? reflectDirection(incomingDirection, orientedNormal);
|
|
1475
|
+
attenuation = transmissionStrength > 1e-3 ? transmission : [1, 1, 1];
|
|
1476
|
+
} else if (eventKind === "transparency") {
|
|
1477
|
+
nextDirection = incomingDirection;
|
|
1478
|
+
const transparencyWeight = Math.max(1 - opacity, transmissionStrength, 0.05);
|
|
1479
|
+
attenuation = transmissionStrength > 1e-3 ? transmission : [transparencyWeight, transparencyWeight, transparencyWeight];
|
|
1480
|
+
} else {
|
|
1481
|
+
nextDirection = normalizeDirection(
|
|
1482
|
+
addVec3(orientedNormal, albedo.map((component) => component - 0.5)),
|
|
1483
|
+
orientedNormal
|
|
1484
|
+
);
|
|
1485
|
+
attenuation = scaleVec3(albedo, Math.max(0.05, 1 - metalness));
|
|
1486
|
+
}
|
|
1487
|
+
const nextThroughput = multiplyVec3(throughput, saturateVec3(attenuation));
|
|
1488
|
+
const continueTracing = eventKind !== "terminate" && colorLuminance(nextThroughput) > 1e-4;
|
|
1489
|
+
return Object.freeze({
|
|
1490
|
+
hitType,
|
|
1491
|
+
eventKind,
|
|
1492
|
+
continueTracing,
|
|
1493
|
+
nextDirection: Object.freeze(nextDirection),
|
|
1494
|
+
attenuation: Object.freeze(saturateVec3(attenuation)),
|
|
1495
|
+
nextThroughput: Object.freeze(nextThroughput),
|
|
1496
|
+
explicitLightSamplingEnabled: Boolean(options.explicitLightSampling)
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1111
1499
|
var lightingImportanceLevels = Object.freeze([
|
|
1112
1500
|
"low",
|
|
1113
1501
|
"medium",
|
|
@@ -1673,6 +2061,91 @@ var lightingWorkerSpecPresets = {
|
|
|
1673
2061
|
}
|
|
1674
2062
|
}
|
|
1675
2063
|
},
|
|
2064
|
+
wavefront: {
|
|
2065
|
+
suggestedAllocationIds: [
|
|
2066
|
+
"lighting.wavefront.active-queue",
|
|
2067
|
+
"lighting.wavefront.next-queue",
|
|
2068
|
+
"lighting.wavefront.accumulation"
|
|
2069
|
+
],
|
|
2070
|
+
jobs: {
|
|
2071
|
+
accumulateTerminalRadiance: {
|
|
2072
|
+
domain: "lighting",
|
|
2073
|
+
importance: "critical",
|
|
2074
|
+
levels: buildWorkerBudgetLevels(
|
|
2075
|
+
"lighting.wavefront.accumulateTerminalRadiance",
|
|
2076
|
+
lightingWorkerQueueClass,
|
|
2077
|
+
{
|
|
2078
|
+
low: {
|
|
2079
|
+
estimatedCostMs: 0.7,
|
|
2080
|
+
maxDispatchesPerFrame: 1,
|
|
2081
|
+
maxJobsPerDispatch: 32,
|
|
2082
|
+
cadenceDivisor: 2,
|
|
2083
|
+
workgroupScale: 0.5,
|
|
2084
|
+
maxQueueDepth: 96
|
|
2085
|
+
},
|
|
2086
|
+
medium: {
|
|
2087
|
+
estimatedCostMs: 1.2,
|
|
2088
|
+
maxDispatchesPerFrame: 1,
|
|
2089
|
+
maxJobsPerDispatch: 64,
|
|
2090
|
+
cadenceDivisor: 1,
|
|
2091
|
+
workgroupScale: 0.75,
|
|
2092
|
+
maxQueueDepth: 192
|
|
2093
|
+
},
|
|
2094
|
+
high: {
|
|
2095
|
+
estimatedCostMs: 1.8,
|
|
2096
|
+
maxDispatchesPerFrame: 2,
|
|
2097
|
+
maxJobsPerDispatch: 128,
|
|
2098
|
+
cadenceDivisor: 1,
|
|
2099
|
+
workgroupScale: 1,
|
|
2100
|
+
maxQueueDepth: 256
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
),
|
|
2104
|
+
suggestedAllocationIds: [
|
|
2105
|
+
"lighting.wavefront.accumulation",
|
|
2106
|
+
"lighting.wavefront.active-queue"
|
|
2107
|
+
]
|
|
2108
|
+
},
|
|
2109
|
+
scatterContinuations: {
|
|
2110
|
+
domain: "lighting",
|
|
2111
|
+
importance: "critical",
|
|
2112
|
+
levels: buildWorkerBudgetLevels(
|
|
2113
|
+
"lighting.wavefront.scatterContinuations",
|
|
2114
|
+
lightingWorkerQueueClass,
|
|
2115
|
+
{
|
|
2116
|
+
low: {
|
|
2117
|
+
estimatedCostMs: 0.8,
|
|
2118
|
+
maxDispatchesPerFrame: 1,
|
|
2119
|
+
maxJobsPerDispatch: 32,
|
|
2120
|
+
cadenceDivisor: 2,
|
|
2121
|
+
workgroupScale: 0.5,
|
|
2122
|
+
maxQueueDepth: 96
|
|
2123
|
+
},
|
|
2124
|
+
medium: {
|
|
2125
|
+
estimatedCostMs: 1.4,
|
|
2126
|
+
maxDispatchesPerFrame: 1,
|
|
2127
|
+
maxJobsPerDispatch: 64,
|
|
2128
|
+
cadenceDivisor: 1,
|
|
2129
|
+
workgroupScale: 0.75,
|
|
2130
|
+
maxQueueDepth: 192
|
|
2131
|
+
},
|
|
2132
|
+
high: {
|
|
2133
|
+
estimatedCostMs: 2.1,
|
|
2134
|
+
maxDispatchesPerFrame: 2,
|
|
2135
|
+
maxJobsPerDispatch: 128,
|
|
2136
|
+
cadenceDivisor: 1,
|
|
2137
|
+
workgroupScale: 1,
|
|
2138
|
+
maxQueueDepth: 256
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
),
|
|
2142
|
+
suggestedAllocationIds: [
|
|
2143
|
+
"lighting.wavefront.active-queue",
|
|
2144
|
+
"lighting.wavefront.next-queue"
|
|
2145
|
+
]
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
1676
2149
|
volumetrics: {
|
|
1677
2150
|
suggestedAllocationIds: [
|
|
1678
2151
|
"lighting.volumetrics.froxel-grid",
|
|
@@ -1882,6 +2355,13 @@ var lightingWorkerDagSpecs = {
|
|
|
1882
2355
|
accumulate: { priority: 3, dependencies: ["pathTrace"] },
|
|
1883
2356
|
denoise: { priority: 2, dependencies: ["accumulate"] }
|
|
1884
2357
|
},
|
|
2358
|
+
wavefront: {
|
|
2359
|
+
accumulateTerminalRadiance: { priority: 3, dependencies: [] },
|
|
2360
|
+
scatterContinuations: {
|
|
2361
|
+
priority: 2,
|
|
2362
|
+
dependencies: ["accumulateTerminalRadiance"]
|
|
2363
|
+
}
|
|
2364
|
+
},
|
|
1885
2365
|
volumetrics: {
|
|
1886
2366
|
volumetricShadow: { priority: 3, dependencies: [] },
|
|
1887
2367
|
froxelIntegrate: { priority: 2, dependencies: ["volumetricShadow"] }
|
|
@@ -1915,6 +2395,16 @@ function resolveLightingQualityDimensions(techniqueName, jobKey) {
|
|
|
1915
2395
|
"pathtracer.pathTrace": { rayTracing: 1, lightingSamples: 1 },
|
|
1916
2396
|
"pathtracer.accumulate": { temporalReuse: 1, updateCadence: 0.4 },
|
|
1917
2397
|
"pathtracer.denoise": { temporalReuse: 1, shading: 0.4 },
|
|
2398
|
+
"wavefront.accumulateTerminalRadiance": {
|
|
2399
|
+
rayTracing: 1,
|
|
2400
|
+
lightingSamples: 1,
|
|
2401
|
+
temporalReuse: 0.4
|
|
2402
|
+
},
|
|
2403
|
+
"wavefront.scatterContinuations": {
|
|
2404
|
+
rayTracing: 1,
|
|
2405
|
+
shading: 0.7,
|
|
2406
|
+
updateCadence: 0.5
|
|
2407
|
+
},
|
|
1918
2408
|
"volumetrics.froxelIntegrate": {
|
|
1919
2409
|
lightingSamples: 0.6,
|
|
1920
2410
|
shading: 0.4,
|
|
@@ -1959,6 +2449,15 @@ function resolveLightingImportanceSignals(techniqueName, jobKey) {
|
|
|
1959
2449
|
},
|
|
1960
2450
|
"pathtracer.accumulate": { visible: true },
|
|
1961
2451
|
"pathtracer.denoise": { visible: true },
|
|
2452
|
+
"wavefront.accumulateTerminalRadiance": {
|
|
2453
|
+
visible: true,
|
|
2454
|
+
shadowSignificance: "high",
|
|
2455
|
+
reflectionSignificance: "high"
|
|
2456
|
+
},
|
|
2457
|
+
"wavefront.scatterContinuations": {
|
|
2458
|
+
visible: true,
|
|
2459
|
+
reflectionSignificance: "high"
|
|
2460
|
+
},
|
|
1962
2461
|
"volumetrics.froxelIntegrate": { visible: true },
|
|
1963
2462
|
"volumetrics.volumetricShadow": { visible: true, shadowSignificance: "high" },
|
|
1964
2463
|
"hdri.irradianceConvolution": { visible: false },
|
|
@@ -2229,9 +2728,12 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
|
|
|
2229
2728
|
createLightingBandPlan,
|
|
2230
2729
|
createLightingProfileModeLadder,
|
|
2231
2730
|
createWavefrontEnvironmentLightingOptions,
|
|
2731
|
+
createWavefrontLightingPlan,
|
|
2232
2732
|
defaultAdaptiveLightingProfilePolicy,
|
|
2233
2733
|
defaultLightingProfile,
|
|
2234
2734
|
defaultLightingTechnique,
|
|
2735
|
+
evaluateWavefrontContinuationEvent,
|
|
2736
|
+
evaluateWavefrontTerminalRadiance,
|
|
2235
2737
|
getLightingProfile,
|
|
2236
2738
|
getLightingProfileWorkerManifest,
|
|
2237
2739
|
getLightingTechnique,
|
|
@@ -2250,8 +2752,17 @@ async function loadLightingProfileWorkerPlan(profileName = defaultLightingProfil
|
|
|
2250
2752
|
lightingProfileModeOrder,
|
|
2251
2753
|
lightingProfileNames,
|
|
2252
2754
|
lightingProfiles,
|
|
2755
|
+
lightingRequiredRendererWavefrontPassOrder,
|
|
2253
2756
|
lightingTechniqueNames,
|
|
2254
2757
|
lightingTechniques,
|
|
2758
|
+
lightingWavefrontBufferContracts,
|
|
2759
|
+
lightingWavefrontContinuationHitTypes,
|
|
2760
|
+
lightingWavefrontHitTypes,
|
|
2761
|
+
lightingWavefrontPassOrder,
|
|
2762
|
+
lightingWavefrontQueuePairStrategy,
|
|
2763
|
+
lightingWavefrontSchemaVersion,
|
|
2764
|
+
lightingWavefrontTerminalHitTypes,
|
|
2765
|
+
lightingWavefrontTerminationPolicy,
|
|
2255
2766
|
lightingWorkerManifests,
|
|
2256
2767
|
lightingWorkerQueueClass,
|
|
2257
2768
|
loadLightingJobs,
|