reze-engine 0.68.0 → 0.68.2

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.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/dist/_dbg-loader.mjs +1100 -0
  3. package/dist/engine.d.ts +114 -0
  4. package/dist/engine.d.ts.map +1 -1
  5. package/dist/engine.js +220 -4
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/shaders/passes/aov.d.ts +37 -0
  10. package/dist/shaders/passes/aov.d.ts.map +1 -0
  11. package/dist/shaders/passes/aov.js +180 -0
  12. package/package.json +1 -1
  13. package/src/engine.ts +259 -6
  14. package/src/index.ts +4 -0
  15. package/src/shaders/passes/aov.ts +187 -0
  16. package/dist/graph/presets/pool_floor.d.ts +0 -3
  17. package/dist/graph/presets/pool_floor.d.ts.map +0 -1
  18. package/dist/graph/presets/pool_floor.js +0 -42
  19. package/dist/graph/presets/water.d.ts +0 -3
  20. package/dist/graph/presets/water.d.ts.map +0 -1
  21. package/dist/graph/presets/water.js +0 -31
  22. package/dist/physics/autofit.d.ts +0 -147
  23. package/dist/physics/autofit.d.ts.map +0 -1
  24. package/dist/physics/autofit.js +0 -501
  25. package/dist/shaders/agx-lut.d.ts +0 -11
  26. package/dist/shaders/agx-lut.d.ts.map +0 -1
  27. package/dist/shaders/agx-lut.js +0 -29
  28. package/dist/shaders/dfg_lut.d.ts +0 -3
  29. package/dist/shaders/dfg_lut.d.ts.map +0 -1
  30. package/dist/shaders/dfg_lut.js +0 -129
  31. package/dist/shaders/ltc_mag_lut.d.ts +0 -3
  32. package/dist/shaders/ltc_mag_lut.d.ts.map +0 -1
  33. package/dist/shaders/ltc_mag_lut.js +0 -1033
  34. package/dist/shaders/passes/field-blit.d.ts +0 -26
  35. package/dist/shaders/passes/field-blit.d.ts.map +0 -1
  36. package/dist/shaders/passes/field-blit.js +0 -65
  37. package/dist/shaders/passes/ghosts.d.ts +0 -22
  38. package/dist/shaders/passes/ghosts.d.ts.map +0 -1
  39. package/dist/shaders/passes/ghosts.js +0 -75
  40. package/dist/shaders/passes/ground-noise.d.ts +0 -7
  41. package/dist/shaders/passes/ground-noise.d.ts.map +0 -1
  42. package/dist/shaders/passes/ground-noise.js +0 -88
  43. package/dist/shaders/passes/sim.d.ts +0 -34
  44. package/dist/shaders/passes/sim.d.ts.map +0 -1
  45. package/dist/shaders/passes/sim.js +0 -169
  46. package/dist/shaders/score-api.d.ts +0 -10
  47. package/dist/shaders/score-api.d.ts.map +0 -1
  48. package/dist/shaders/score-api.js +0 -114
  49. package/dist/shaders/shadow-api.d.ts +0 -29
  50. package/dist/shaders/shadow-api.d.ts.map +0 -1
  51. package/dist/shaders/shadow-api.js +0 -112
package/dist/engine.js CHANGED
@@ -17,6 +17,7 @@ import { RezePhysics } from "./physics";
17
17
  import { createFetchAssetReader, createFileMapAssetReader, deriveBasePathFromPmxPath, fileListToMap, findFirstPmxFileInList, joinAssetPath, normalizeAssetPath, } from "./asset-reader";
18
18
  import { SHADOW_DEPTH_SHADER_WGSL } from "./shaders/passes/shadow";
19
19
  import { ID_DEBUG_SHADER_WGSL } from "./shaders/passes/id-debug";
20
+ import { AOV_PIXELS_WGSL, AOV_PIXEL_FIELDS, AOV_PIXEL_STRIDE, AOV_PROBE_HEAD, AOV_PROBE_STRIDE, AOV_PROBE_WGSL } from "./shaders/passes/aov";
20
21
  import { paramChanged, sampleParamTrack } from "./param-track";
21
22
  import { advanceSim, DISSOLVE_PARAMS, dissolveConstants, dissolveCycleOf, effectState, sampleDissolveCycle, scheduledDissolve, } from "./effect-schedule";
22
23
  import { parentKeySpan } from "./parent-keys";
@@ -851,6 +852,11 @@ export class Engine {
851
852
  this.idDebugBindGroupLayout = null;
852
853
  this.idDebugBindGroup = null;
853
854
  this.idDebug = false;
855
+ /** Store ids and depth for readAovs — see setAovCapture. */
856
+ this.aovCapture = false;
857
+ this.aovPixelsPipeline = null;
858
+ this.aovProbePipeline = null;
859
+ this.aovSampler = null;
854
860
  this.particleFrame = 0;
855
861
  /** Reused for the per-frame field-clock upload — one 16-byte write per
856
862
  * drawing effect, and allocating a fresh array for each would be garbage
@@ -2071,6 +2077,187 @@ export class Engine {
2071
2077
  hasObjectIds() {
2072
2078
  return this.idView !== null;
2073
2079
  }
2080
+ /**
2081
+ * Keep the frame's ids and depth for readAovs.
2082
+ *
2083
+ * Both are discarded at the end of an ordinary frame (see the store ops in
2084
+ * renderFrame); while this is on they are stored, on exactly the terms the
2085
+ * id-debug view and a masking effect already get. Turn it on, render the
2086
+ * frame to measure, read it, turn it off — the stores cost real bandwidth
2087
+ * and nothing else reads them.
2088
+ *
2089
+ * Returns false when the device has no id attachment, in which case
2090
+ * readAovs answers null.
2091
+ */
2092
+ setAovCapture(on) {
2093
+ this.aovCapture = on && this.idView !== null;
2094
+ return this.aovCapture || !on;
2095
+ }
2096
+ /**
2097
+ * What drew each id: the model's object id, and its materials' ids in the
2098
+ * order the id attachment counts them (materials that produced a draw,
2099
+ * 1-based). The floor is GROUND_OBJECT_ID / GROUND_MATERIAL_ID.
2100
+ */
2101
+ getIdLegend() {
2102
+ const out = [];
2103
+ for (const [name, inst] of this.modelInstances) {
2104
+ const materials = [];
2105
+ let id = 0;
2106
+ for (const mat of inst.model.getMaterials()) {
2107
+ if (mat.vertexCount === 0)
2108
+ continue;
2109
+ materials.push({ id: ++id, name: mat.name });
2110
+ }
2111
+ out.push({ objectId: inst.objectId, model: name, materials });
2112
+ }
2113
+ return out;
2114
+ }
2115
+ /**
2116
+ * The last frame, as numbers: per texel its linear colour, what drew it, how
2117
+ * far it is, and the light arriving there — the sun's shadow and facing, the
2118
+ * lamps', the world's. See shaders/passes/aov.ts for what each field is and
2119
+ * how the normal is rebuilt.
2120
+ *
2121
+ * Reads the frame ALREADY RENDERED, at the render size it was rendered at:
2122
+ * call it after renderFrame with setAovCapture on, before anything renders
2123
+ * again (a capture stops the loop for exactly this). Null when the device
2124
+ * has no id attachment or capture was not on.
2125
+ */
2126
+ async readAovs() {
2127
+ if (!this.aovCapture || !this.idView || !this.depthReadView || !this.camera)
2128
+ return null;
2129
+ const width = this.hdrResolveTexture.width;
2130
+ const height = this.hdrResolveTexture.height;
2131
+ const pipeline = this.aovPixelsPipeline ?? (this.aovPixelsPipeline = this.device.createComputePipeline({
2132
+ label: "aov pixels",
2133
+ layout: "auto",
2134
+ compute: { module: this.device.createShaderModule({ label: "aov pixels", code: AOV_PIXELS_WGSL }), entryPoint: "pixels" },
2135
+ }));
2136
+ const invVP = this.camera.getProjectionMatrix().multiply(this.camera.getViewMatrix()).inverse().values;
2137
+ const eye = this.camera.getPosition();
2138
+ const uniform = new ArrayBuffer(64 + 16 + 16);
2139
+ new Float32Array(uniform, 0, 16).set(invVP);
2140
+ new Float32Array(uniform, 64, 4).set([eye.x, eye.y, eye.z, 1]);
2141
+ new Uint32Array(uniform, 80, 4).set([width, height, 0, 0]);
2142
+ const ubuf = this.device.createBuffer({ label: "aov uniforms", size: uniform.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
2143
+ this.device.queue.writeBuffer(ubuf, 0, uniform);
2144
+ const texels = width * height;
2145
+ const fBytes = texels * AOV_PIXEL_STRIDE * 4;
2146
+ const idBytes = texels * 4;
2147
+ const fBuf = this.device.createBuffer({ label: "aov out", size: fBytes, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
2148
+ const idBuf = this.device.createBuffer({ label: "aov ids", size: idBytes, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
2149
+ const fRead = this.device.createBuffer({ label: "aov out read", size: fBytes, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
2150
+ const idRead = this.device.createBuffer({ label: "aov ids read", size: idBytes, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
2151
+ const bind = this.device.createBindGroup({
2152
+ label: "aov pixels",
2153
+ layout: pipeline.getBindGroupLayout(0),
2154
+ entries: [
2155
+ ...this.aovLightEntries(),
2156
+ { binding: 7, resource: this.idView },
2157
+ { binding: 8, resource: this.depthReadView },
2158
+ { binding: 9, resource: this.hdrResolveTexture.createView() },
2159
+ { binding: 10, resource: { buffer: ubuf } },
2160
+ { binding: 11, resource: { buffer: fBuf } },
2161
+ { binding: 12, resource: { buffer: idBuf } },
2162
+ ],
2163
+ });
2164
+ const encoder = this.device.createCommandEncoder({ label: "aov pixels" });
2165
+ const pass = encoder.beginComputePass({ label: "aov pixels" });
2166
+ pass.setPipeline(pipeline);
2167
+ pass.setBindGroup(0, bind);
2168
+ pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
2169
+ pass.end();
2170
+ encoder.copyBufferToBuffer(fBuf, 0, fRead, 0, fBytes);
2171
+ encoder.copyBufferToBuffer(idBuf, 0, idRead, 0, idBytes);
2172
+ this.device.queue.submit([encoder.finish()]);
2173
+ try {
2174
+ await Promise.all([fRead.mapAsync(GPUMapMode.READ), idRead.mapAsync(GPUMapMode.READ)]);
2175
+ const f = new Float32Array(fRead.getMappedRange().slice(0));
2176
+ const ids = new Uint32Array(idRead.getMappedRange().slice(0));
2177
+ const exposure = this.getViewTransformOptions().exposure ?? 0;
2178
+ return { width, height, stride: AOV_PIXEL_STRIDE, fields: AOV_PIXEL_FIELDS, data: f, ids, exposure };
2179
+ }
2180
+ finally {
2181
+ for (const b of [ubuf, fBuf, idBuf, fRead, idRead])
2182
+ b.destroy();
2183
+ }
2184
+ }
2185
+ /**
2186
+ * The light arriving at points the caller names — a face, a hand, a spot on
2187
+ * the floor — facing the normal given: the sun's shadow, facing and colour,
2188
+ * the world's ambient, and EACH LAMP'S share kept apart, in the order the
2189
+ * engine holds them (the document's lamps first, as setLights got them,
2190
+ * then any an effect emits).
2191
+ *
2192
+ * Reads the shadow atlas and lamp records of the last frame rendered.
2193
+ */
2194
+ async probeLight(points) {
2195
+ if (points.length === 0)
2196
+ return [];
2197
+ const pipeline = this.aovProbePipeline ?? (this.aovProbePipeline = this.device.createComputePipeline({
2198
+ label: "aov probe",
2199
+ layout: "auto",
2200
+ compute: { module: this.device.createShaderModule({ label: "aov probe", code: AOV_PROBE_WGSL }), entryPoint: "probe" },
2201
+ }));
2202
+ const data = new Float32Array(points.length * 8);
2203
+ points.forEach((pt, i) => data.set([pt.position.x, pt.position.y, pt.position.z, 1, pt.normal.x, pt.normal.y, pt.normal.z, 0], i * 8));
2204
+ const inBuf = this.device.createBuffer({ label: "aov probe points", size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
2205
+ this.device.queue.writeBuffer(inBuf, 0, data);
2206
+ const outBytes = points.length * AOV_PROBE_STRIDE * 4;
2207
+ const outBuf = this.device.createBuffer({ label: "aov probe out", size: outBytes, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });
2208
+ const readBuf = this.device.createBuffer({ label: "aov probe read", size: outBytes, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
2209
+ const bind = this.device.createBindGroup({
2210
+ label: "aov probe",
2211
+ layout: pipeline.getBindGroupLayout(0),
2212
+ entries: [...this.aovLightEntries(), { binding: 7, resource: { buffer: inBuf } }, { binding: 8, resource: { buffer: outBuf } }],
2213
+ });
2214
+ const encoder = this.device.createCommandEncoder({ label: "aov probe" });
2215
+ const pass = encoder.beginComputePass({ label: "aov probe" });
2216
+ pass.setPipeline(pipeline);
2217
+ pass.setBindGroup(0, bind);
2218
+ pass.dispatchWorkgroups(points.length);
2219
+ pass.end();
2220
+ encoder.copyBufferToBuffer(outBuf, 0, readBuf, 0, outBytes);
2221
+ this.device.queue.submit([encoder.finish()]);
2222
+ try {
2223
+ await readBuf.mapAsync(GPUMapMode.READ);
2224
+ const f = new Float32Array(readBuf.getMappedRange().slice(0));
2225
+ return points.map((_, i) => {
2226
+ const o = i * AOV_PROBE_STRIDE;
2227
+ const count = Math.round(f[o + 8]);
2228
+ const lamps = [];
2229
+ for (let l = 0; l < count; l++) {
2230
+ const b = o + AOV_PROBE_HEAD + l * 4;
2231
+ lamps.push([f[b], f[b + 1], f[b + 2]]);
2232
+ }
2233
+ return {
2234
+ sunShadow: f[o],
2235
+ sunFacing: f[o + 1],
2236
+ sun: [f[o + 2], f[o + 3], f[o + 4]],
2237
+ ambient: [f[o + 5], f[o + 6], f[o + 7]],
2238
+ lamps,
2239
+ documentLamps: Math.round(f[o + 9]),
2240
+ };
2241
+ });
2242
+ }
2243
+ finally {
2244
+ for (const b of [inBuf, outBuf, readBuf])
2245
+ b.destroy();
2246
+ }
2247
+ }
2248
+ /** The scene light API and the lamps, as aov.ts's prelude binds them. */
2249
+ aovLightEntries() {
2250
+ this.aovSampler ?? (this.aovSampler = this.device.createSampler({ label: "aov cookie sampler", magFilter: "linear", minFilter: "linear" }));
2251
+ return [
2252
+ { binding: 0, resource: { buffer: this.lightUniformBuffer } },
2253
+ { binding: 1, resource: { buffer: this.shadowLightVPBuffer } },
2254
+ { binding: 2, resource: this.shadowAtlasView },
2255
+ { binding: 3, resource: this.shadowComparisonSampler },
2256
+ { binding: 4, resource: { buffer: this.lightsBuffer } },
2257
+ { binding: 5, resource: this.cookieView },
2258
+ { binding: 6, resource: this.aovSampler },
2259
+ ];
2260
+ }
2074
2261
  /** The pass that draws it, built lazily so a scene that never asks for the
2075
2262
  * debug view never compiles it. */
2076
2263
  ensureIdDebugPipeline() {
@@ -5550,6 +5737,11 @@ export class Engine {
5550
5737
  * frame. Nothing recompiles, so this is safe to drive per frame from a
5551
5738
  * timeline.
5552
5739
  */
5740
+ /** How many effects are installed — the indices setEffectInfluence and its
5741
+ * siblings address are 0 up to this. */
5742
+ getEffectCount() {
5743
+ return this.effects.length;
5744
+ }
5553
5745
  setEffectInfluence(index, influence) {
5554
5746
  const fx = this.effects[index];
5555
5747
  if (!fx)
@@ -11433,6 +11625,27 @@ export class Engine {
11433
11625
  inst.simulateWhileHidden = on;
11434
11626
  return true;
11435
11627
  }
11628
+ /**
11629
+ * Whether one model casts a shadow at all.
11630
+ *
11631
+ * Off takes it out of the shadow map, the shadow the cast lays on a stage and
11632
+ * the caster sphere the floor fits its shadow to; it still receives shadows
11633
+ * from everything else. On defers to the PMX author's flag per material, so a
11634
+ * material its author excluded stays excluded.
11635
+ */
11636
+ setModelCastShadow(modelName, on) {
11637
+ const inst = this.modelInstances.get(modelName);
11638
+ if (!inst)
11639
+ return false;
11640
+ if (inst.castShadow === on)
11641
+ return true;
11642
+ inst.castShadow = on;
11643
+ this.sortDrawCalls(inst);
11644
+ return true;
11645
+ }
11646
+ getModelCastShadow(modelName) {
11647
+ return this.modelInstances.get(modelName)?.castShadow ?? true;
11648
+ }
11436
11649
  getPhysicsFloor() {
11437
11650
  return this.physicsFloor;
11438
11651
  }
@@ -11657,7 +11870,7 @@ export class Engine {
11657
11870
  this.cullMetaF32[f + 4] = b[3];
11658
11871
  this.cullMetaF32[f + 5] = b[4];
11659
11872
  this.cullMetaF32[f + 6] = b[5];
11660
- this.cullMetaU32[f + 7] = draw.castsShadow === true ? Engine.CULL_DRAW_CASTS_SHADOW : 0;
11873
+ this.cullMetaU32[f + 7] = inst.castShadow && draw.castsShadow === true ? Engine.CULL_DRAW_CASTS_SHADOW : 0;
11661
11874
  // Everything but instanceCount is structural, so the compute never writes
11662
11875
  // it — one fewer store per draw per frame, and the args stay readable in a
11663
11876
  // capture as "this is the draw, that is whether it survived".
@@ -12762,6 +12975,7 @@ export class Engine {
12762
12975
  name,
12763
12976
  model,
12764
12977
  simulateWhileHidden: false,
12978
+ castShadow: true,
12765
12979
  basePath,
12766
12980
  assetReader,
12767
12981
  gpuBuffers,
@@ -14699,7 +14913,7 @@ export class Engine {
14699
14913
  // The scattering pass is the third reader: it rejects taps across a depth
14700
14914
  // step and scales its width by distance, so a scene wearing a subsurface
14701
14915
  // look stores depth for as long as it does.
14702
- const depthRead = dofOn || this.effects.some((e) => e.hasForeground) || this.subsurfaceInUse();
14916
+ const depthRead = dofOn || this.aovCapture || this.effects.some((e) => e.hasForeground) || this.subsurfaceInUse();
14703
14917
  this.renderPassDescriptor.depthStencilAttachment.depthStoreOp = depthRead ? "store" : "discard";
14704
14918
  if (depthRead)
14705
14919
  this.writeDepthOfFieldUniforms();
@@ -14721,7 +14935,7 @@ export class Engine {
14721
14935
  if (idAtt) {
14722
14936
  // The flood seeds from the id attachment, so an effect that reads only the
14723
14937
  // DISTANCE still needs the ids kept — it just never names them itself.
14724
- const idsRead = this.idDebug || this.effects.some((e) => e.readsIds || e.readsCastDistance);
14938
+ const idsRead = this.idDebug || this.aovCapture || this.effects.some((e) => e.readsIds || e.readsCastDistance);
14725
14939
  idAtt.storeOp = idsRead ? "store" : "discard";
14726
14940
  }
14727
14941
  // The stage's reflection probe, when one is due: its own submits, ahead of
@@ -15714,7 +15928,9 @@ export class Engine {
15714
15928
  ground: 4,
15715
15929
  };
15716
15930
  inst.drawCalls.sort((a, b) => typeOrder[a.type] - typeOrder[b.type] || this.drawCallRank(inst, a) - this.drawCallRank(inst, b));
15717
- inst.shadowDrawCalls = inst.drawCalls.filter((d) => (d.type === "opaque" || d.type === "transparent") && d.castsShadow === true);
15931
+ inst.shadowDrawCalls = inst.castShadow
15932
+ ? inst.drawCalls.filter((d) => (d.type === "opaque" || d.type === "transparent") && d.castsShadow === true)
15933
+ : [];
15718
15934
  // The sort reorders drawCalls, and a draw's position in that array is its
15719
15935
  // slot in every cull buffer.
15720
15936
  this.cullListDirty = true;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_DEPTH_OF_FIELD_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, type ColorGradingOptions, type StageGradeLut, type SceneFog, type StageCastShadow, type SceneFogLayer, type EngineStats, type EngineOptions, type BloomOptions, type DepthOfFieldOptions, type ViewTransformOptions, type ViewTransformName, type LoadModelFromFilesOptions, type MaterialPreset, type MaterialPresetMap, type ModelTransform, type SceneLight, RENDERING_LAYER_DEFAULT, RENDERING_LAYER_CHARACTER, type ModelAttachment, type GizmoDragEvent, type GizmoDragCallback, type GizmoDragKind, type DissolveCycle, type EffectParamValue, type EffectResult, type CullDiagnostics, type MidiNote, } from "./engine";
1
+ export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_DEPTH_OF_FIELD_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, type ColorGradingOptions, type StageGradeLut, type SceneFog, type StageCastShadow, type SceneFogLayer, type EngineStats, type EngineOptions, type BloomOptions, type DepthOfFieldOptions, type ViewTransformOptions, type ViewTransformName, type LoadModelFromFilesOptions, type MaterialPreset, type MaterialPresetMap, type ModelTransform, type SceneLight, RENDERING_LAYER_DEFAULT, RENDERING_LAYER_CHARACTER, type ModelAttachment, type SceneAovs, type LightProbe, type GizmoDragEvent, type GizmoDragCallback, type GizmoDragKind, type DissolveCycle, type EffectParamValue, type EffectResult, type CullDiagnostics, type MidiNote, } from "./engine";
2
2
  export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload";
3
3
  export { parseMidi } from "./midi-loader";
4
4
  export { parseHDR, type HdrImage } from "./hdr";
@@ -59,4 +59,6 @@ export type { EyeTrackingOptions } from "./model";
59
59
  export type { NativeStagePackage, NativeStageReader } from "./unity/stage";
60
60
  export type { NativeLook, NativeMaterialSpec, NativeTexture } from "./unity/looks";
61
61
  export type { NativeShader, NativeShaderInfo } from "./unity/host";
62
+ export { AOV_PIXEL_FIELDS, AOV_PIXEL_STRIDE } from "./shaders/passes/aov";
63
+ export { GROUND_MATERIAL_ID, GROUND_OBJECT_ID } from "./shaders/passes/ground";
62
64
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -66,3 +66,5 @@ export { boneOverlay, boneMarkerPositions, rigidbodyOverlay, jointOverlay, build
66
66
  * Pure strings, no GPU: importing them costs a host nothing.
67
67
  */
68
68
  export { EFFECT_MATH_API, PARTICLE_STRUCT_WGSL } from "./shaders/passes/hosted-api";
69
+ export { AOV_PIXEL_FIELDS, AOV_PIXEL_STRIDE } from "./shaders/passes/aov";
70
+ export { GROUND_MATERIAL_ID, GROUND_OBJECT_ID } from "./shaders/passes/ground";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The frame, read back as numbers — what an agent (or a test) measures instead
3
+ * of squinting at a picture.
4
+ *
5
+ * A screenshot answers "what does it look like". It cannot answer the questions
6
+ * a lighting artist actually asks of a render: is her face in the sun's shadow,
7
+ * which lamp is carrying the key, how many stops over is that highlight, which
8
+ * pixels are skin. Every one of those is a number the frame already computed
9
+ * and threw away; these passes keep them.
10
+ *
11
+ * TWO ENTRY POINTS over one prelude:
12
+ *
13
+ * pixels — one invocation per texel of the frame just rendered. Reads the id
14
+ * attachment, the depth and the linear HDR resolve, rebuilds the
15
+ * world point from the depth and a normal from its neighbours, and
16
+ * asks the same shadow atlas and lamp records the materials asked.
17
+ * probe — the same light questions at points the caller names (a bone, a
18
+ * face), with each lamp's share kept apart.
19
+ *
20
+ * The normal is RECONSTRUCTED from depth, not the one the material shaded
21
+ * with: the frame never stores normals, and storing them for every frame to
22
+ * serve an occasional readback would be the cost the id attachment's
23
+ * store-only-when-read rule exists to avoid. On a silhouette edge it is the
24
+ * flatter of the two neighbours, which is right for every texel that is not
25
+ * the edge itself.
26
+ */
27
+ /** Floats per texel in the pixels output — see PIXEL_FIELDS. */
28
+ export declare const AOV_PIXEL_STRIDE = 12;
29
+ /** What each of a texel's floats is, in order. */
30
+ export declare const AOV_PIXEL_FIELDS: readonly ["r", "g", "b", "depth", "sunShadow", "sunFacing", "lampR", "lampG", "lampB", "ambR", "ambG", "ambB"];
31
+ /** Floats per probe point before its per-lamp block. */
32
+ export declare const AOV_PROBE_HEAD = 12;
33
+ /** Floats per probe point: the head, then rgb+pad for every lamp slot. */
34
+ export declare const AOV_PROBE_STRIDE: number;
35
+ export declare const AOV_PIXELS_WGSL: string;
36
+ export declare const AOV_PROBE_WGSL: string;
37
+ //# sourceMappingURL=aov.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aov.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/aov.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,gHAOnB,CAAA;AAEV,wDAAwD;AACxD,eAAO,MAAM,cAAc,KAAK,CAAA;AAChC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,QAAkC,CAAA;AAqB/D,eAAO,MAAM,eAAe,QA6E3B,CAAA;AAED,eAAO,MAAM,cAAc,QAwC1B,CAAA"}
@@ -0,0 +1,180 @@
1
+ import { sceneLightApi } from "../scene-light-api";
2
+ import { lightsApi, MAX_LIGHTS } from "../lights";
3
+ /**
4
+ * The frame, read back as numbers — what an agent (or a test) measures instead
5
+ * of squinting at a picture.
6
+ *
7
+ * A screenshot answers "what does it look like". It cannot answer the questions
8
+ * a lighting artist actually asks of a render: is her face in the sun's shadow,
9
+ * which lamp is carrying the key, how many stops over is that highlight, which
10
+ * pixels are skin. Every one of those is a number the frame already computed
11
+ * and threw away; these passes keep them.
12
+ *
13
+ * TWO ENTRY POINTS over one prelude:
14
+ *
15
+ * pixels — one invocation per texel of the frame just rendered. Reads the id
16
+ * attachment, the depth and the linear HDR resolve, rebuilds the
17
+ * world point from the depth and a normal from its neighbours, and
18
+ * asks the same shadow atlas and lamp records the materials asked.
19
+ * probe — the same light questions at points the caller names (a bone, a
20
+ * face), with each lamp's share kept apart.
21
+ *
22
+ * The normal is RECONSTRUCTED from depth, not the one the material shaded
23
+ * with: the frame never stores normals, and storing them for every frame to
24
+ * serve an occasional readback would be the cost the id attachment's
25
+ * store-only-when-read rule exists to avoid. On a silhouette edge it is the
26
+ * flatter of the two neighbours, which is right for every texel that is not
27
+ * the edge itself.
28
+ */
29
+ /** Floats per texel in the pixels output — see PIXEL_FIELDS. */
30
+ export const AOV_PIXEL_STRIDE = 12;
31
+ /** What each of a texel's floats is, in order. */
32
+ export const AOV_PIXEL_FIELDS = [
33
+ "r", "g", "b", // linear HDR, before exposure and the view transform
34
+ "depth", // distance from the eye, world units; 0 where nothing drew
35
+ "sunShadow", // 1 lit, 0 in a caster's shadow; -1 where nothing drew
36
+ "sunFacing", // N·L toward the sun, -1..1 (the toon ramp's input)
37
+ "lampR", "lampG", "lampB", // every positional lamp's light arriving here
38
+ "ambR", "ambG", "ambB", // the world's light arriving here
39
+ ];
40
+ /** Floats per probe point before its per-lamp block. */
41
+ export const AOV_PROBE_HEAD = 12;
42
+ /** Floats per probe point: the head, then rgb+pad for every lamp slot. */
43
+ export const AOV_PROBE_STRIDE = AOV_PROBE_HEAD + MAX_LIGHTS * 4;
44
+ // Bindings 0..3 are the scene light API, 4..6 the lamps and their cookies.
45
+ const PRELUDE = /* wgsl */ `
46
+ ${sceneLightApi(true, 0, 0)}
47
+ ${lightsApi(0, 4, "0xffffffffu", { binding: 5, sampler: "_aovSampler" })}
48
+ @group(0) @binding(6) var _aovSampler: sampler;
49
+
50
+ /** The sun's cast shadow at p, nudged along n off the surface — the
51
+ * materials' rzSunOcclusion, without the facing test (sunFacing carries it). */
52
+ fn aovSunShadow(p: vec3f, n: vec3f) -> f32 {
53
+ let castAmt = _rzLight.lights[0].direction.w;
54
+ if (castAmt <= 0.0) { return 1.0; }
55
+ let a = _rzSunLocate(p + n * 0.08);
56
+ if (a.w < 0.0) { return 1.0; }
57
+ return mix(1.0, _rzSunTaps(a), castAmt);
58
+ }
59
+
60
+ fn aovToSun() -> vec3f { return normalize(-_rzLight.lights[0].direction.xyz); }
61
+ `;
62
+ export const AOV_PIXELS_WGSL = /* wgsl */ `
63
+ ${PRELUDE}
64
+ @group(0) @binding(7) var idTex: texture_multisampled_2d<u32>;
65
+ @group(0) @binding(8) var depthTex: texture_depth_multisampled_2d;
66
+ @group(0) @binding(9) var hdrTex: texture_2d<f32>;
67
+ struct AovU { invViewProj: mat4x4f, eye: vec4f, size: vec4u, }
68
+ @group(0) @binding(10) var<uniform> u: AovU;
69
+ @group(0) @binding(11) var<storage, read_write> outF: array<f32>;
70
+ @group(0) @binding(12) var<storage, read_write> outId: array<u32>;
71
+
72
+ fn worldAt(c: vec2i, z: f32) -> vec3f {
73
+ let uv = (vec2f(c) + 0.5) / vec2f(u.size.xy);
74
+ let h = u.invViewProj * vec4f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, z, 1.0);
75
+ return h.xyz / h.w;
76
+ }
77
+
78
+ fn drawn(c: vec2i) -> bool {
79
+ if (any(c < vec2i(0)) || any(c >= vec2i(u.size.xy))) { return false; }
80
+ let id = textureLoad(idTex, c, 0).xy;
81
+ return id.x != 0u || id.y != 0u;
82
+ }
83
+
84
+ /** The step from p to the neighbour at c + d (or c - d, negated), whichever is
85
+ * nearer in depth: across a silhouette the far neighbour belongs to something
86
+ * else, and a normal built from it tilts toward the background. */
87
+ fn tangent(c: vec2i, d: vec2i, p: vec3f) -> vec3f {
88
+ let a = c + d;
89
+ let b = c - d;
90
+ let okA = drawn(a);
91
+ let okB = drawn(b);
92
+ var ta = vec3f(0.0);
93
+ var tb = vec3f(0.0);
94
+ if (okA) { ta = worldAt(a, textureLoad(depthTex, a, 0)) - p; }
95
+ if (okB) { tb = p - worldAt(b, textureLoad(depthTex, b, 0)); }
96
+ if (okA && okB) { return select(tb, ta, dot(ta, ta) <= dot(tb, tb)); }
97
+ if (okA) { return ta; }
98
+ return tb;
99
+ }
100
+
101
+ @compute @workgroup_size(8, 8)
102
+ fn pixels(@builtin(global_invocation_id) gid: vec3u) {
103
+ if (gid.x >= u.size.x || gid.y >= u.size.y) { return; }
104
+ let c = vec2i(gid.xy);
105
+ let i = gid.y * u.size.x + gid.x;
106
+ let o = i * ${AOV_PIXEL_STRIDE}u;
107
+ let id = textureLoad(idTex, c, 0).xy;
108
+ let rgb = textureLoad(hdrTex, c, 0).rgb;
109
+ // Material in the low half, object in the high — the attachment's own order
110
+ // (scene-contract: vec2u(material, object)).
111
+ outId[i] = (id.y << 16u) | (id.x & 0xffffu);
112
+ outF[o] = rgb.r;
113
+ outF[o + 1u] = rgb.g;
114
+ outF[o + 2u] = rgb.b;
115
+ for (var k = 3u; k < ${AOV_PIXEL_STRIDE}u; k++) { outF[o + k] = 0.0; }
116
+ if (id.x == 0u && id.y == 0u) {
117
+ outF[o + 4u] = -1.0;
118
+ return;
119
+ }
120
+ let p = worldAt(c, textureLoad(depthTex, c, 0));
121
+ var n = cross(tangent(c, vec2i(0, 1), p), tangent(c, vec2i(1, 0), p));
122
+ let toEye = u.eye.xyz - p;
123
+ if (dot(n, n) < 1e-12) { n = toEye; }
124
+ n = normalize(n);
125
+ // Toward the eye: the side of the surface this texel shows.
126
+ if (dot(n, toEye) < 0.0) { n = -n; }
127
+ let lamps = rzLightsDiffuse(p, n);
128
+ let amb = rzWorldAmbient(n);
129
+ outF[o + 3u] = length(toEye);
130
+ outF[o + 4u] = aovSunShadow(p, n);
131
+ outF[o + 5u] = dot(n, aovToSun());
132
+ outF[o + 6u] = lamps.r;
133
+ outF[o + 7u] = lamps.g;
134
+ outF[o + 8u] = lamps.b;
135
+ outF[o + 9u] = amb.r;
136
+ outF[o + 10u] = amb.g;
137
+ outF[o + 11u] = amb.b;
138
+ }
139
+ `;
140
+ export const AOV_PROBE_WGSL = /* wgsl */ `
141
+ ${PRELUDE}
142
+ struct ProbePoint { p: vec4f, n: vec4f, }
143
+ @group(0) @binding(7) var<storage, read> points: array<ProbePoint>;
144
+ @group(0) @binding(8) var<storage, read_write> outF: array<f32>;
145
+
146
+ @compute @workgroup_size(1)
147
+ fn probe(@builtin(global_invocation_id) gid: vec3u) {
148
+ let i = gid.x;
149
+ if (i >= arrayLength(&points)) { return; }
150
+ let p = points[i].p.xyz;
151
+ let n = normalize(points[i].n.xyz);
152
+ let o = i * ${AOV_PROBE_STRIDE}u;
153
+ // Colour and strength travel apart in the light uniform (writeSun); the
154
+ // light arriving is their product.
155
+ let sun = _rzLight.lights[0].color.rgb * _rzLight.lights[0].color.w;
156
+ let amb = rzWorldAmbient(n);
157
+ outF[o] = aovSunShadow(p, n);
158
+ outF[o + 1u] = dot(n, aovToSun());
159
+ outF[o + 2u] = sun.r;
160
+ outF[o + 3u] = sun.g;
161
+ outF[o + 4u] = sun.b;
162
+ outF[o + 5u] = amb.r;
163
+ outF[o + 6u] = amb.g;
164
+ outF[o + 7u] = amb.b;
165
+ let count = rzLightCount();
166
+ outF[o + 8u] = f32(count);
167
+ // The document's lamps come first (setLights order); the rest are effects'.
168
+ outF[o + 9u] = f32(_rzLightDocCount());
169
+ for (var k = 10u; k < ${AOV_PROBE_HEAD}u; k++) { outF[o + k] = 0.0; }
170
+ for (var l = 0u; l < ${MAX_LIGHTS}u; l++) {
171
+ var c = vec3f(0.0);
172
+ if (l < count) { c = _rzLightOne(l, p, n); }
173
+ let b = o + ${AOV_PROBE_HEAD}u + l * 4u;
174
+ outF[b] = c.r;
175
+ outF[b + 1u] = c.g;
176
+ outF[b + 2u] = c.b;
177
+ outF[b + 3u] = 0.0;
178
+ }
179
+ }
180
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.68.0",
3
+ "version": "0.68.2",
4
4
  "description": "A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",