@tinypoly/gpu-renderer 0.0.4 → 0.1.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.
Files changed (140) hide show
  1. package/ARCHITECTURE.md +81 -10
  2. package/README.md +53 -5
  3. package/dist/atmosphere.d.ts +21 -0
  4. package/dist/atmosphere.d.ts.map +1 -0
  5. package/dist/atmosphere.js +72 -0
  6. package/dist/atmosphere.js.map +1 -0
  7. package/dist/bvh.d.ts +11 -6
  8. package/dist/bvh.d.ts.map +1 -1
  9. package/dist/bvh.js +64 -11
  10. package/dist/bvh.js.map +1 -1
  11. package/dist/denoise.d.ts +17 -2
  12. package/dist/denoise.d.ts.map +1 -1
  13. package/dist/denoise.js +95 -27
  14. package/dist/denoise.js.map +1 -1
  15. package/dist/dispatchScheduler.d.ts +11 -4
  16. package/dist/dispatchScheduler.d.ts.map +1 -1
  17. package/dist/dispatchScheduler.js +14 -7
  18. package/dist/dispatchScheduler.js.map +1 -1
  19. package/dist/frameLayout.d.ts.map +1 -1
  20. package/dist/frameLayout.js +18 -0
  21. package/dist/frameLayout.js.map +1 -1
  22. package/dist/geometry.d.ts.map +1 -1
  23. package/dist/geometry.js +6 -3
  24. package/dist/geometry.js.map +1 -1
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +3 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/lensPass.d.ts +60 -0
  30. package/dist/lensPass.d.ts.map +1 -0
  31. package/dist/lensPass.js +235 -0
  32. package/dist/lensPass.js.map +1 -0
  33. package/dist/lightTree.d.ts +43 -0
  34. package/dist/lightTree.d.ts.map +1 -0
  35. package/dist/lightTree.js +107 -0
  36. package/dist/lightTree.js.map +1 -0
  37. package/dist/lights.d.ts +19 -1
  38. package/dist/lights.d.ts.map +1 -1
  39. package/dist/lights.js +63 -1
  40. package/dist/lights.js.map +1 -1
  41. package/dist/materialLayout.d.ts +24 -0
  42. package/dist/materialLayout.d.ts.map +1 -1
  43. package/dist/materialLayout.js +36 -1
  44. package/dist/materialLayout.js.map +1 -1
  45. package/dist/pathTracePass.d.ts +6 -3
  46. package/dist/pathTracePass.d.ts.map +1 -1
  47. package/dist/pathTracePass.js +16 -10
  48. package/dist/pathTracePass.js.map +1 -1
  49. package/dist/renderer.d.ts +19 -7
  50. package/dist/renderer.d.ts.map +1 -1
  51. package/dist/renderer.js +198 -36
  52. package/dist/renderer.js.map +1 -1
  53. package/dist/sceneUpload.d.ts +19 -1
  54. package/dist/sceneUpload.d.ts.map +1 -1
  55. package/dist/sceneUpload.js +25 -3
  56. package/dist/sceneUpload.js.map +1 -1
  57. package/dist/settings.d.ts +136 -0
  58. package/dist/settings.d.ts.map +1 -0
  59. package/dist/settings.js +100 -0
  60. package/dist/settings.js.map +1 -0
  61. package/dist/subsurface.d.ts +76 -0
  62. package/dist/subsurface.d.ts.map +1 -0
  63. package/dist/subsurface.js +70 -0
  64. package/dist/subsurface.js.map +1 -0
  65. package/dist/texturePack.d.ts +16 -4
  66. package/dist/texturePack.d.ts.map +1 -1
  67. package/dist/texturePack.js +61 -8
  68. package/dist/texturePack.js.map +1 -1
  69. package/dist/volumeContainment.d.ts +17 -0
  70. package/dist/volumeContainment.d.ts.map +1 -0
  71. package/dist/volumeContainment.js +61 -0
  72. package/dist/volumeContainment.js.map +1 -0
  73. package/dist/wgsl/bsdf.wgsl.d.ts +1 -1
  74. package/dist/wgsl/bsdf.wgsl.d.ts.map +1 -1
  75. package/dist/wgsl/bsdf.wgsl.js +35 -3
  76. package/dist/wgsl/bsdf.wgsl.js.map +1 -1
  77. package/dist/wgsl/bvh.wgsl.d.ts +8 -2
  78. package/dist/wgsl/bvh.wgsl.d.ts.map +1 -1
  79. package/dist/wgsl/bvh.wgsl.js +167 -96
  80. package/dist/wgsl/bvh.wgsl.js.map +1 -1
  81. package/dist/wgsl/common.wgsl.d.ts.map +1 -1
  82. package/dist/wgsl/common.wgsl.js +24 -7
  83. package/dist/wgsl/common.wgsl.js.map +1 -1
  84. package/dist/wgsl/lens.wgsl.d.ts +17 -0
  85. package/dist/wgsl/lens.wgsl.d.ts.map +1 -0
  86. package/dist/wgsl/lens.wgsl.js +315 -0
  87. package/dist/wgsl/lens.wgsl.js.map +1 -0
  88. package/dist/wgsl/lights.wgsl.d.ts +1 -1
  89. package/dist/wgsl/lights.wgsl.d.ts.map +1 -1
  90. package/dist/wgsl/lights.wgsl.js +206 -33
  91. package/dist/wgsl/lights.wgsl.js.map +1 -1
  92. package/dist/wgsl/pathTrace.wgsl.d.ts +1 -1
  93. package/dist/wgsl/pathTrace.wgsl.d.ts.map +1 -1
  94. package/dist/wgsl/pathTrace.wgsl.js +410 -25
  95. package/dist/wgsl/pathTrace.wgsl.js.map +1 -1
  96. package/dist/wgsl/random.wgsl.d.ts +1 -1
  97. package/dist/wgsl/random.wgsl.d.ts.map +1 -1
  98. package/dist/wgsl/random.wgsl.js +9 -17
  99. package/dist/wgsl/random.wgsl.js.map +1 -1
  100. package/dist/wgsl/sampling.wgsl.d.ts +1 -1
  101. package/dist/wgsl/sampling.wgsl.d.ts.map +1 -1
  102. package/dist/wgsl/sampling.wgsl.js +25 -0
  103. package/dist/wgsl/sampling.wgsl.js.map +1 -1
  104. package/dist/wgsl/surface.wgsl.d.ts +1 -1
  105. package/dist/wgsl/surface.wgsl.d.ts.map +1 -1
  106. package/dist/wgsl/surface.wgsl.js +47 -4
  107. package/dist/wgsl/surface.wgsl.js.map +1 -1
  108. package/dist/wgsl/texture.wgsl.d.ts +1 -1
  109. package/dist/wgsl/texture.wgsl.d.ts.map +1 -1
  110. package/dist/wgsl/texture.wgsl.js +10 -11
  111. package/dist/wgsl/texture.wgsl.js.map +1 -1
  112. package/package.json +1 -1
  113. package/src/atmosphere.ts +95 -0
  114. package/src/bvh.ts +81 -12
  115. package/src/denoise.ts +110 -27
  116. package/src/dispatchScheduler.ts +16 -7
  117. package/src/frameLayout.ts +18 -0
  118. package/src/geometry.ts +6 -2
  119. package/src/index.ts +13 -0
  120. package/src/lensPass.ts +299 -0
  121. package/src/lightTree.ts +172 -0
  122. package/src/lights.ts +90 -2
  123. package/src/materialLayout.ts +52 -1
  124. package/src/pathTracePass.ts +19 -10
  125. package/src/renderer.ts +258 -43
  126. package/src/sceneUpload.ts +41 -4
  127. package/src/settings.ts +234 -0
  128. package/src/subsurface.ts +153 -0
  129. package/src/texturePack.ts +80 -13
  130. package/src/volumeContainment.ts +81 -0
  131. package/src/wgsl/bsdf.wgsl.ts +35 -3
  132. package/src/wgsl/bvh.wgsl.ts +167 -96
  133. package/src/wgsl/common.wgsl.ts +24 -7
  134. package/src/wgsl/lens.wgsl.ts +314 -0
  135. package/src/wgsl/lights.wgsl.ts +206 -33
  136. package/src/wgsl/pathTrace.wgsl.ts +410 -25
  137. package/src/wgsl/random.wgsl.ts +9 -17
  138. package/src/wgsl/sampling.wgsl.ts +25 -0
  139. package/src/wgsl/surface.wgsl.ts +47 -4
  140. package/src/wgsl/texture.wgsl.ts +10 -11
@@ -1,4 +1,5 @@
1
1
  import { layoutStruct, writeStruct, type StructField } from "./structLayout.js";
2
+ import { bevelOf, subsurfaceOf, volumeOf } from "./subsurface.js";
2
3
  import { NO_TEXTURE } from "./texturePack.js";
3
4
  import type { SerializedMaterial, UniformValue } from "./upstream.js";
4
5
 
@@ -59,6 +60,20 @@ export interface MaterialProperties {
59
60
  combine: number;
60
61
  side: number;
61
62
  blending: number;
63
+ /**
64
+ * Share of the diffuse layer that scatters light inside the material, from `userData.cpuRenderer.subsurface`; zero
65
+ * for none. Physical and standard materials only.
66
+ */
67
+ subsurface: number;
68
+ /** Mean free path of red, green and blue inside the material, in world units. */
69
+ subsurfaceRadius: number[];
70
+ subsurfaceAnisotropy: number;
71
+ /** The medium a volume's boundary encloses: its single scattering albedo, density per scene unit and anisotropy. */
72
+ volumeAlbedo: number[];
73
+ volumeDensity: number;
74
+ volumeAnisotropy: number;
75
+ /** The radius over which edges are rounded at shading time, in scene units; zero leaves them sharp. */
76
+ bevel: number;
62
77
  /** An index into the texture table per slot, or `NO_TEXTURE`. */
63
78
  maps: Record<MaterialMap, number>;
64
79
  }
@@ -137,6 +152,18 @@ export const MATERIAL_ALPHA_HASH = 2048;
137
152
  /** A physical material, whose specular colour and intensity reach the lobe and whose grazing reflectance follows them. */
138
153
  export const MATERIAL_SPECULAR = 4096;
139
154
 
155
+ /**
156
+ * The mesh only bounds a medium, from `userData.cpuRenderer.volume`: its surface is invisible and the space inside
157
+ * scatters light.
158
+ */
159
+ export const MATERIAL_VOLUME = 8192;
160
+
161
+ /**
162
+ * An unlit material whose colour is known without running anything: opaque, normal blending, no light map, environment
163
+ * map, alpha map or shader. What it shows is what it emits, so direct sampling can aim at it like an emissive one.
164
+ */
165
+ export const MATERIAL_UNLIT_LIGHT = 16384;
166
+
140
167
  /** What a material's `program` reads as when it has no shader. */
141
168
  export const NO_PROGRAM = 0xffffffff;
142
169
 
@@ -186,6 +213,11 @@ const FIELDS: StructField[] = [
186
213
  { name: "dielectric", type: "f32" },
187
214
  { name: "afterProgram", type: "u32", doc: "What a hook does after <opaque_fragment>, or NO_PROGRAM." },
188
215
  { name: "afterData", type: "u32" },
216
+ { name: "subsurface", type: "vec4f", doc: "rgb is the mean free path of each colour inside, w the share of the diffuse layer." },
217
+ { name: "subsurfaceAnisotropy", type: "f32" },
218
+ { name: "volume", type: "vec4f", doc: "rgb is the medium's single scattering albedo, w its density per scene unit." },
219
+ { name: "volumeAnisotropy", type: "f32" },
220
+ { name: "bevel", type: "f32", doc: "The radius edges are rounded over at shading time; zero leaves them sharp." },
189
221
  { name: "normalRow0", type: "vec4f", doc: "The mesh's world normal matrix, for an object space normal map." },
190
222
  { name: "normalRow1", type: "vec4f" },
191
223
  { name: "normalRow2", type: "vec4f" },
@@ -263,6 +295,8 @@ export function materialProperties(
263
295
  const sheen = physical ? Math.max(0, number(values.sheen, 0)) : 0;
264
296
  const anisotropy = physical ? number(values.anisotropy, 0) : 0;
265
297
  const thickness = vector(values.iridescenceThicknessRange, [100, 400], 2);
298
+ const medium = pbr ? subsurfaceOf(material) : null;
299
+ const volume = volumeOf(material);
266
300
 
267
301
  const maps = Object.fromEntries(MATERIAL_MAPS.map(slot => {
268
302
  const id = material?.maps?.[slot];
@@ -285,7 +319,12 @@ export function materialProperties(
285
319
  | (number(values.normalMapType, 0) === 1 ? MATERIAL_OBJECT_NORMAL_MAP : 0)
286
320
  | (shader?.patched ? MATERIAL_PATCHED : 0)
287
321
  | (flag(values.alphaHash, false) ? MATERIAL_ALPHA_HASH : 0)
288
- | (physical ? MATERIAL_SPECULAR : 0);
322
+ | (physical ? MATERIAL_SPECULAR : 0)
323
+ | (volume ? MATERIAL_VOLUME : 0)
324
+ | (type === "MeshBasicMaterial" && !shader && !transparent && !flag(values.alphaHash, false)
325
+ && !flag(values.wireframe, false) && number(values.blending, 1) === 1 && number(values.opacity, 1) >= 1
326
+ && number(values.alphaTest, 0) <= 0 && maps.alphaMap === NO_TEXTURE && maps.envMap === NO_TEXTURE
327
+ && maps.lightMap === NO_TEXTURE && !volume ? MATERIAL_UNLIT_LIGHT : 0);
289
328
 
290
329
  return {
291
330
  kind,
@@ -330,6 +369,13 @@ export function materialProperties(
330
369
  combine: number(values.combine, 0),
331
370
  side: number(values.side, 0),
332
371
  blending: number(values.blending, 1),
372
+ subsurface: medium?.weight ?? 0,
373
+ subsurfaceRadius: medium?.radius ?? [1, 1, 1],
374
+ subsurfaceAnisotropy: medium?.anisotropy ?? 0,
375
+ volumeAlbedo: volume?.albedo ?? [1, 1, 1],
376
+ volumeDensity: volume?.density ?? 0,
377
+ volumeAnisotropy: volume?.anisotropy ?? 0,
378
+ bevel: LIT.has(type) ? bevelOf(material) : 0,
333
379
  maps,
334
380
  };
335
381
  }
@@ -400,6 +446,11 @@ export function writeMaterial(
400
446
  dielectric: p.dielectric,
401
447
  afterProgram: placement.afterProgram,
402
448
  afterData: placement.afterData,
449
+ subsurface: [...p.subsurfaceRadius, p.subsurface],
450
+ subsurfaceAnisotropy: p.subsurfaceAnisotropy,
451
+ volume: [...p.volumeAlbedo, p.volumeDensity],
452
+ volumeAnisotropy: p.volumeAnisotropy,
453
+ bevel: p.bevel,
403
454
  normalRow0: [n[0], n[3], n[6], 0],
404
455
  normalRow1: [n[1], n[4], n[7], 0],
405
456
  normalRow2: [n[2], n[5], n[8], 0],
@@ -23,8 +23,12 @@ export type FrameUniforms = Record<keyof typeof FRAME_LAYOUT.offsets & string, n
23
23
  * declarations. The scene's translated shaders sit between the texture code they call and the surface code that calls
24
24
  * them.
25
25
  */
26
- export function pathTraceSource(programs: ShaderPrograms | null = null): string {
27
- return COMMON_WGSL + RANDOM_WGSL + BVH_WGSL + SAMPLING_WGSL + TEXTURE_WGSL + SHADER_SUPPORT_WGSL
26
+ export function pathTraceSource(
27
+ programs: ShaderPrograms | null = null, subsurface = true, volumes = true, bevel = true): string {
28
+ // The random walk inside materials and the media inside volumes are compiled in only for a scene that has them: the
29
+ // kernel is bound by its size, and either would cost every other scene a few percent for code that never runs.
30
+ return `const SUBSURFACE_WALK: bool = ${subsurface};\nconst VOLUMES: bool = ${volumes};\nconst BEVEL: bool = ${bevel};\n`
31
+ + COMMON_WGSL + RANDOM_WGSL + BVH_WGSL + SAMPLING_WGSL + TEXTURE_WGSL + SHADER_SUPPORT_WGSL
28
32
  + programsWgsl(programs) + ENVIRONMENT_WGSL + SURFACE_WGSL + BSDF_WGSL + LIGHTS_WGSL + PATH_TRACE_WGSL;
29
33
  }
30
34
 
@@ -37,26 +41,31 @@ export class PathTracePass {
37
41
  private readonly uniforms: GPUBuffer;
38
42
  private readonly values = new ArrayBuffer(FRAME_BYTES);
39
43
  private bindGroup: GPUBindGroup | null = null;
40
- private programKey = "";
44
+ private programKey = "|false|false|false";
41
45
 
42
46
  constructor(private readonly device: GPUDevice) {
43
- this.pipeline = this.build(null);
47
+ this.pipeline = this.build(null, false, false, false);
44
48
  this.uniforms = device.createBuffer({ size: FRAME_BYTES, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
45
49
  }
46
50
 
47
- /** Recompiles the kernel when the scene's shaders changed; the same set of programs keeps the pipeline it has. */
48
- setPrograms(programs: ShaderPrograms | null) {
49
- const key = programs?.key ?? "";
51
+ /**
52
+ * Recompiles the kernel when the scene's shaders changed, or whether it has a material that scatters light inside;
53
+ * the same set keeps the pipeline it has.
54
+ */
55
+ setPrograms(programs: ShaderPrograms | null, subsurface = false, volumes = false, bevel = false) {
56
+ const key = `${programs?.key ?? ""}|${subsurface}|${volumes}|${bevel}`;
50
57
 
51
58
  if (key === this.programKey) return;
52
59
 
53
60
  this.programKey = key;
54
- this.pipeline = this.build(programs);
61
+ this.pipeline = this.build(programs, subsurface, volumes, bevel);
55
62
  this.bindGroup = null;
56
63
  }
57
64
 
58
- private build(programs: ShaderPrograms | null): GPUComputePipeline {
59
- const module = createShaderModule(this.device, "path trace", pathTraceSource(programs));
65
+ private build(programs: ShaderPrograms | null, subsurface: boolean, volumes: boolean,
66
+ bevel: boolean): GPUComputePipeline {
67
+ const module = createShaderModule(this.device, "path trace",
68
+ pathTraceSource(programs, subsurface, volumes, bevel));
60
69
 
61
70
  return this.device.createComputePipeline({ layout: "auto", compute: { module, entryPoint: "main" } });
62
71
  }