@plasius/gpu-renderer 0.1.12 → 0.1.14

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/src/index.js CHANGED
@@ -1,5 +1,25 @@
1
1
  const DEFAULT_CLEAR_COLOR = Object.freeze([0.07, 0.11, 0.18, 1.0]);
2
2
  const DEFAULT_CANVAS_SELECTOR = "canvas[data-plasius-gpu-renderer]";
3
+ export {
4
+ createDefaultWavefrontSceneObjects,
5
+ createWavefrontBvhBuildLevels,
6
+ createWavefrontBvhSortStages,
7
+ createWavefrontEmissiveTriangleIndexSource,
8
+ createWavefrontGpuMeshSource,
9
+ createWavefrontMeshAcceleration,
10
+ createWavefrontPathTracingComputeConfig,
11
+ createWavefrontPathTracingComputeRenderer,
12
+ estimateWavefrontPathTracingMemory,
13
+ normalizeWavefrontMesh,
14
+ normalizeWavefrontSceneObject,
15
+ packWavefrontBvhNodes,
16
+ packWavefrontSceneObjects,
17
+ packWavefrontTriangles,
18
+ supportsWavefrontPathTracingCompute,
19
+ wavefrontMaterialKinds,
20
+ wavefrontPathTracingComputeLimits,
21
+ wavefrontSceneObjectKinds,
22
+ } from "./wavefront-compute.js";
3
23
  export const rendererDebugOwner = "renderer";
4
24
  export const rendererWorkerQueueClass = "render";
5
25
  export const defaultRendererWorkerProfile = "realtime";
@@ -27,6 +47,23 @@ export const rendererRayTracingStageOrder = Object.freeze([
27
47
  "composition",
28
48
  "present",
29
49
  ]);
50
+ export const rendererWavefrontBufferSchemaVersion = 1;
51
+ export const rendererWavefrontQueuePairStrategy = "ping-pong-active-next";
52
+ export const rendererWavefrontHitTypes = Object.freeze([
53
+ "surface",
54
+ "emissive",
55
+ "environment",
56
+ "transparent",
57
+ "miss",
58
+ ]);
59
+ export const rendererWavefrontPassOrder = Object.freeze([
60
+ "generatePrimaryRays",
61
+ "intersectActiveQueue",
62
+ "resolveSurfaceRecords",
63
+ "accumulateTerminalRadiance",
64
+ "scatterContinuations",
65
+ "compactAndSwapQueues",
66
+ ]);
30
67
 
31
68
  const rendererRayTracingStageDefinitions = Object.freeze(
32
69
  rendererRayTracingStageOrder.map((key, index) =>
@@ -104,6 +141,167 @@ const rendererAccelerationStructurePolicies = Object.freeze(
104
141
  )
105
142
  );
106
143
 
144
+ function createWavefrontField(name, type, description) {
145
+ return Object.freeze({
146
+ name,
147
+ type,
148
+ description,
149
+ });
150
+ }
151
+
152
+ const rendererWavefrontBufferContracts = Object.freeze({
153
+ ray: Object.freeze({
154
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
155
+ recordName: "RayRecord",
156
+ fields: Object.freeze([
157
+ createWavefrontField("rayId", "u32", "Stable ray identifier for correlation and debugging."),
158
+ createWavefrontField("parentRayId", "u32", "Parent ray identifier for continuation lineage."),
159
+ createWavefrontField("sourcePixelId", "u32", "Screen pixel or texel that owns the sample."),
160
+ createWavefrontField("sampleId", "u32", "Per-pixel sample slot for accumulation."),
161
+ createWavefrontField("bounce", "u32", "Breadth-first bounce depth for the queue entry."),
162
+ createWavefrontField("origin", "vec3<f32>", "Ray origin in renderer world space."),
163
+ createWavefrontField("direction", "vec3<f32>", "Normalized ray direction in renderer world space."),
164
+ createWavefrontField("throughput", "vec3<f32>", "Current path throughput before the next event."),
165
+ createWavefrontField("mediumRefId", "u32", "Active medium reference identifier for the ray."),
166
+ createWavefrontField("flags", "u32", "Bit flags for front-face state, debug, and quality toggles."),
167
+ ]),
168
+ }),
169
+ hit: Object.freeze({
170
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
171
+ recordName: "HitRecord",
172
+ fields: Object.freeze([
173
+ createWavefrontField("rayId", "u32", "Ray identifier copied from the active queue."),
174
+ createWavefrontField("sourcePixelId", "u32", "Pixel/texel owner for the ray sample."),
175
+ createWavefrontField("hitType", rendererWavefrontHitTypes.join(" | "), "Resolved hit classification for termination or continuation."),
176
+ createWavefrontField("distance", "f32", "Nearest-hit distance or miss sentinel."),
177
+ createWavefrontField("entityId", "u32", "Stable scene entity identifier."),
178
+ createWavefrontField("instanceId", "u32", "Renderer instance identifier."),
179
+ createWavefrontField("primitiveId", "u32", "Primitive or triangle identifier."),
180
+ createWavefrontField("materialId", "u32", "Surface material identifier."),
181
+ createWavefrontField("barycentrics", "vec3<f32>", "Triangle barycentric coordinates for interpolation."),
182
+ createWavefrontField("uv", "vec2<f32>", "Resolved surface UV when available."),
183
+ createWavefrontField("geometricNormal", "vec3<f32>", "True geometric face normal."),
184
+ createWavefrontField("shadingNormal", "vec3<f32>", "Interpolated or repaired shading normal."),
185
+ createWavefrontField("frontFace", "bool", "Front-face classification for shading and medium transitions."),
186
+ ]),
187
+ }),
188
+ surface: Object.freeze({
189
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
190
+ recordName: "SurfaceRecord",
191
+ fields: Object.freeze([
192
+ createWavefrontField("rayId", "u32", "Ray identifier matched to the resolved hit."),
193
+ createWavefrontField("entityId", "u32", "Stable scene entity identifier."),
194
+ createWavefrontField("materialRefId", "u32", "Material-reference indirection for shading lookup tables."),
195
+ createWavefrontField("mediumRefId", "u32", "Resolved medium transition/reference identifier."),
196
+ createWavefrontField("geometricNormal", "vec3<f32>", "Preserved geometric normal for hemisphere checks."),
197
+ createWavefrontField("shadingNormal", "vec3<f32>", "Normal used for BSDF/BTDF evaluation."),
198
+ createWavefrontField("uv", "vec2<f32>", "Resolved texture coordinate."),
199
+ createWavefrontField("tangentFrame", "mat3x3<f32>", "Optional tangent basis for normal-map transforms."),
200
+ ]),
201
+ }),
202
+ materialReference: Object.freeze({
203
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
204
+ recordName: "MaterialReferenceRecord",
205
+ fields: Object.freeze([
206
+ createWavefrontField("materialRefId", "u32", "Stable material lookup identifier."),
207
+ createWavefrontField("materialId", "u32", "Authoritative material id from scene submission."),
208
+ createWavefrontField("shadingModel", "u32", "Renderer-owned shading model enum."),
209
+ createWavefrontField("textureSetId", "u32", "Texture indirection set for the material."),
210
+ createWavefrontField("flags", "u32", "Alpha, emissive, transmission, and debug flags."),
211
+ ]),
212
+ }),
213
+ mediumReference: Object.freeze({
214
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
215
+ recordName: "MediumReferenceRecord",
216
+ fields: Object.freeze([
217
+ createWavefrontField("mediumRefId", "u32", "Stable medium lookup identifier."),
218
+ createWavefrontField("mediumId", "u32", "Authoritative medium or fluid descriptor id."),
219
+ createWavefrontField("phaseModel", "u32", "Medium phase-function selector."),
220
+ createWavefrontField("absorption", "vec3<f32>", "Absorption coefficients for the active medium."),
221
+ createWavefrontField("scattering", "vec3<f32>", "Scattering coefficients for the active medium."),
222
+ ]),
223
+ }),
224
+ accumulation: Object.freeze({
225
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
226
+ recordName: "AccumulationRecord",
227
+ fields: Object.freeze([
228
+ createWavefrontField("sourcePixelId", "u32", "Screen pixel or texel accumulator owner."),
229
+ createWavefrontField("sampleCount", "u32", "Committed sample count for the pixel."),
230
+ createWavefrontField("radiance", "vec3<f32>", "Accumulated radiance before tone-map/output resolve."),
231
+ createWavefrontField("throughput", "vec3<f32>", "Last surviving throughput for debug and variance tracking."),
232
+ createWavefrontField("resetEpoch", "u32", "Accumulation reset generation for history invalidation."),
233
+ ]),
234
+ }),
235
+ });
236
+
237
+ function buildWavefrontTerminationPolicy() {
238
+ return Object.freeze({
239
+ terminalHitTypes: Object.freeze(["emissive", "environment", "miss"]),
240
+ continuationHitTypes: Object.freeze(["surface", "transparent"]),
241
+ emissive: Object.freeze({
242
+ action: "accumulate-and-stop",
243
+ contributesRadiance: true,
244
+ }),
245
+ environment: Object.freeze({
246
+ action: "accumulate-and-stop",
247
+ contributesRadiance: true,
248
+ }),
249
+ miss: Object.freeze({
250
+ action: "accumulate-environment-or-dark-stop",
251
+ contributesRadiance: true,
252
+ }),
253
+ });
254
+ }
255
+
256
+ function buildWavefrontBounceSchedule(maxDepth) {
257
+ return Object.freeze(
258
+ Array.from({ length: maxDepth }, (_, index) =>
259
+ Object.freeze({
260
+ bounce: index,
261
+ readQueue: index % 2 === 0 ? "active" : "next",
262
+ writeQueue: index % 2 === 0 ? "next" : "active",
263
+ passOrder: rendererWavefrontPassOrder,
264
+ })
265
+ )
266
+ );
267
+ }
268
+
269
+ export function createWavefrontPathTracingPlan(options = {}) {
270
+ const maxDepth =
271
+ options.maxDepth === undefined
272
+ ? 6
273
+ : readPositiveInteger("maxDepth", options.maxDepth);
274
+ const queueCapacity =
275
+ options.queueCapacity === undefined
276
+ ? 8192
277
+ : readPositiveInteger("queueCapacity", options.queueCapacity);
278
+ const accumulationResetEpoch =
279
+ options.accumulationResetEpoch === undefined
280
+ ? 0
281
+ : readNonNegativeInteger("accumulationResetEpoch", options.accumulationResetEpoch);
282
+ const explicitLightSampling = options.explicitLightSampling === true;
283
+
284
+ return Object.freeze({
285
+ schemaVersion: rendererWavefrontBufferSchemaVersion,
286
+ owner: rendererDebugOwner,
287
+ maxDepth,
288
+ queueCapacity,
289
+ explicitLightSampling,
290
+ accumulationResetEpoch,
291
+ queueLayout: Object.freeze({
292
+ strategy: rendererWavefrontQueuePairStrategy,
293
+ compactAfterScatter: true,
294
+ queues: Object.freeze([
295
+ Object.freeze({ name: "active", role: "current-bounce" }),
296
+ Object.freeze({ name: "next", role: "next-bounce" }),
297
+ ]),
298
+ }),
299
+ bufferContracts: rendererWavefrontBufferContracts,
300
+ bounceSchedule: buildWavefrontBounceSchedule(maxDepth),
301
+ terminationPolicy: buildWavefrontTerminationPolicy(),
302
+ });
303
+ }
304
+
107
305
  function buildRendererWorkerBudgetLevels(jobType, queueClass, levels) {
108
306
  return Object.freeze(
109
307
  levels.map((level) =>
@@ -678,6 +876,7 @@ export function createRayTracingRenderPlan(options = {}) {
678
876
  renderStages: workerManifest.renderStages,
679
877
  representationBands: representations,
680
878
  accelerationStructureUpdates: workerManifest.accelerationStructureUpdates,
879
+ wavefront: createWavefrontPathTracingPlan(options.wavefront),
681
880
  workerManifest,
682
881
  });
683
882
  }
@@ -732,6 +931,20 @@ function readPositiveNumber(name, value) {
732
931
  return value;
733
932
  }
734
933
 
934
+ function readPositiveInteger(name, value) {
935
+ if (!Number.isInteger(value) || value <= 0) {
936
+ throw new Error(`${name} must be a positive integer.`);
937
+ }
938
+ return value;
939
+ }
940
+
941
+ function readNonNegativeInteger(name, value) {
942
+ if (!Number.isInteger(value) || value < 0) {
943
+ throw new Error(`${name} must be a non-negative integer.`);
944
+ }
945
+ return value;
946
+ }
947
+
735
948
  function now() {
736
949
  if (typeof performance !== "undefined" && typeof performance.now === "function") {
737
950
  return performance.now();