@multiplekex/shallot 0.2.4 → 0.3.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 (62) hide show
  1. package/package.json +1 -1
  2. package/src/core/component.ts +1 -1
  3. package/src/core/index.ts +1 -13
  4. package/src/core/math.ts +186 -0
  5. package/src/core/state.ts +1 -1
  6. package/src/core/xml.ts +56 -41
  7. package/src/extras/arrows/index.ts +3 -3
  8. package/src/extras/caustic.ts +37 -0
  9. package/src/extras/gradient/index.ts +63 -69
  10. package/src/extras/index.ts +3 -0
  11. package/src/extras/lines/index.ts +3 -3
  12. package/src/extras/orbit/index.ts +1 -1
  13. package/src/extras/skylab/index.ts +314 -0
  14. package/src/extras/text/font.ts +69 -14
  15. package/src/extras/text/index.ts +17 -69
  16. package/src/extras/text/sdf.ts +13 -2
  17. package/src/extras/water/index.ts +119 -0
  18. package/src/standard/defaults.ts +2 -0
  19. package/src/standard/index.ts +2 -0
  20. package/src/standard/raster/batch.ts +149 -0
  21. package/src/standard/raster/forward.ts +832 -0
  22. package/src/standard/raster/index.ts +191 -0
  23. package/src/standard/raster/shadow.ts +408 -0
  24. package/src/standard/{render → raytracing}/bvh/blas.ts +336 -88
  25. package/src/standard/raytracing/bvh/radix.ts +473 -0
  26. package/src/standard/raytracing/bvh/refit.ts +711 -0
  27. package/src/standard/{render → raytracing}/bvh/structs.ts +0 -55
  28. package/src/standard/{render → raytracing}/bvh/tlas.ts +155 -140
  29. package/src/standard/{render → raytracing}/bvh/traverse.ts +72 -64
  30. package/src/standard/{render → raytracing}/depth.ts +9 -9
  31. package/src/standard/raytracing/index.ts +409 -0
  32. package/src/standard/{render → raytracing}/instance.ts +31 -16
  33. package/src/standard/{render → raytracing}/ray.ts +1 -1
  34. package/src/standard/raytracing/shaders.ts +798 -0
  35. package/src/standard/{render → raytracing}/triangle.ts +1 -1
  36. package/src/standard/render/camera.ts +96 -106
  37. package/src/standard/render/data.ts +1 -1
  38. package/src/standard/render/index.ts +136 -220
  39. package/src/standard/render/indirect.ts +9 -10
  40. package/src/standard/render/light.ts +2 -2
  41. package/src/standard/render/mesh.ts +404 -0
  42. package/src/standard/render/overlay.ts +8 -5
  43. package/src/standard/render/pass.ts +1 -1
  44. package/src/standard/render/postprocess.ts +263 -242
  45. package/src/standard/render/scene.ts +28 -16
  46. package/src/standard/render/surface/index.ts +81 -12
  47. package/src/standard/render/surface/shaders.ts +511 -0
  48. package/src/standard/render/surface/structs.ts +23 -6
  49. package/src/standard/tween/tween.ts +44 -115
  50. package/src/standard/render/bvh/radix.ts +0 -476
  51. package/src/standard/render/forward/index.ts +0 -259
  52. package/src/standard/render/forward/raster.ts +0 -228
  53. package/src/standard/render/mesh/box.ts +0 -20
  54. package/src/standard/render/mesh/index.ts +0 -446
  55. package/src/standard/render/mesh/plane.ts +0 -11
  56. package/src/standard/render/mesh/sphere.ts +0 -40
  57. package/src/standard/render/mesh/unified.ts +0 -96
  58. package/src/standard/render/shaders.ts +0 -484
  59. package/src/standard/render/surface/compile.ts +0 -67
  60. package/src/standard/render/surface/noise.ts +0 -45
  61. package/src/standard/render/surface/wgsl.ts +0 -573
  62. /package/src/standard/{render → raytracing}/intersection.ts +0 -0
@@ -58,53 +58,6 @@ export function leafIndex(child: number): number {
58
58
 
59
59
  export const LEAF_FLAG_WGSL = /* wgsl */ `const LEAF_FLAG: u32 = 0x80000000u;`;
60
60
 
61
- export const TRIANGLE_STRUCT_WGSL = /* wgsl */ `
62
- struct Triangle {
63
- v0: vec3<f32>,
64
- entityId: u32,
65
- e1: vec3<f32>,
66
- _pad0: u32,
67
- e2: vec3<f32>,
68
- _pad1: u32,
69
- n0_enc: u32,
70
- n1_enc: u32,
71
- n2_enc: u32,
72
- _pad2: u32,
73
- }
74
-
75
- fn octEncode(n: vec3<f32>) -> u32 {
76
- let absSum = abs(n.x) + abs(n.y) + abs(n.z);
77
- var vx = n.x / absSum;
78
- var vy = n.y / absSum;
79
- let vz = n.z / absSum;
80
- if (vz < 0.0) {
81
- let signX = select(-1.0, 1.0, vx >= 0.0);
82
- let signY = select(-1.0, 1.0, vy >= 0.0);
83
- let newVx = (1.0 - abs(vy)) * signX;
84
- let newVy = (1.0 - abs(vx)) * signY;
85
- vx = newVx;
86
- vy = newVy;
87
- }
88
- let x = u32(clamp((vx * 0.5 + 0.5) * 65535.0, 0.0, 65535.0));
89
- let y = u32(clamp((vy * 0.5 + 0.5) * 65535.0, 0.0, 65535.0));
90
- return (y << 16u) | x;
91
- }
92
-
93
- fn octDecode(enc: u32) -> vec3<f32> {
94
- let x = f32(enc & 0xFFFFu) / 65535.0 * 2.0 - 1.0;
95
- let y = f32(enc >> 16u) / 65535.0 * 2.0 - 1.0;
96
- let z = 1.0 - abs(x) - abs(y);
97
- var n: vec3<f32>;
98
- if (z < 0.0) {
99
- let signX = select(-1.0, 1.0, x >= 0.0);
100
- let signY = select(-1.0, 1.0, y >= 0.0);
101
- n = vec3<f32>((1.0 - abs(y)) * signX, (1.0 - abs(x)) * signY, z);
102
- } else {
103
- n = vec3<f32>(x, y, z);
104
- }
105
- return normalize(n);
106
- }`;
107
-
108
61
  export const TREE_NODE_STRUCT_WGSL = /* wgsl */ `
109
62
  struct TreeNode {
110
63
  minX: f32,
@@ -131,16 +84,8 @@ struct BVHNode {
131
84
 
132
85
  export const TREE_NODE_SIZE = 32;
133
86
  export const BVH_NODE_SIZE = 128;
134
- export const BLAS_NODE_SIZE = 32;
135
- export const BLAS_TRIANGLE_SIZE = 64;
136
87
  export const INVALID_NODE = 0xffffffff;
137
88
 
138
- export const BLAS_NODE_STRUCT_WGSL = /* wgsl */ `
139
- struct BLASNode {
140
- minX: f32, minY: f32, minZ: f32, leftChild: u32,
141
- maxX: f32, maxY: f32, maxZ: f32, rightChild: u32,
142
- }`;
143
-
144
89
  export const BLAS_TRIANGLE_STRUCT_WGSL = /* wgsl */ `
145
90
  struct BLASTriangle {
146
91
  v0: vec3<f32>, _pad0: u32,
@@ -1,6 +1,6 @@
1
1
  import { MAX_ENTITIES } from "../../../core";
2
2
  import type { ComputeNode, ExecutionContext } from "../../compute";
3
- import { createRadixSort } from "./radix";
3
+ import { createRadixSort, dispatchRadixSort } from "./radix";
4
4
  import {
5
5
  TREE_NODE_STRUCT_WGSL,
6
6
  BVH_NODE_STRUCT_WGSL,
@@ -721,12 +721,159 @@ export interface TLASConfig {
721
721
  instanceCount: GPUBuffer;
722
722
  tlas: TLASBuffers;
723
723
  getEntityCount: () => number;
724
+ getRaytracing?: () => boolean;
725
+ }
726
+
727
+ interface TLASGPU {
728
+ pipelines: TLASPipelines;
729
+ bindGroups: TLASBindGroups;
730
+ radixSort: Awaited<ReturnType<typeof createRadixSort>>;
731
+ }
732
+
733
+ async function prepareTLAS(device: GPUDevice, config: TLASConfig): Promise<TLASGPU> {
734
+ const [boundsModule, mortonModule, treeModule, propagateModule, collapseModule] =
735
+ await Promise.all([
736
+ device.createShaderModule({ code: boundsShader }),
737
+ device.createShaderModule({ code: mortonShader }),
738
+ device.createShaderModule({ code: treeShader }),
739
+ device.createShaderModule({ code: propagateShader }),
740
+ device.createShaderModule({ code: collapseShader }),
741
+ ]);
742
+
743
+ const [bounds, morton, tree, propagate, collapse, radixSort] = await Promise.all([
744
+ device.createComputePipelineAsync({
745
+ layout: "auto",
746
+ compute: { module: boundsModule, entryPoint: "main" },
747
+ }),
748
+ device.createComputePipelineAsync({
749
+ layout: "auto",
750
+ compute: { module: mortonModule, entryPoint: "main" },
751
+ }),
752
+ device.createComputePipelineAsync({
753
+ layout: "auto",
754
+ compute: { module: treeModule, entryPoint: "main" },
755
+ }),
756
+ device.createComputePipelineAsync({
757
+ layout: "auto",
758
+ compute: { module: propagateModule, entryPoint: "main" },
759
+ }),
760
+ device.createComputePipelineAsync({
761
+ layout: "auto",
762
+ compute: { module: collapseModule, entryPoint: "main" },
763
+ }),
764
+ createRadixSort(device, {
765
+ keys: config.tlas.mortonCodes,
766
+ values: config.tlas.instanceIds,
767
+ count: MAX_ENTITIES,
768
+ }),
769
+ ]);
770
+
771
+ const pipelines = { bounds, morton, tree, propagate, collapse };
772
+
773
+ const bindGroups: TLASBindGroups = {
774
+ bounds: device.createBindGroup({
775
+ layout: pipelines.bounds.getBindGroupLayout(0),
776
+ entries: [
777
+ { binding: 0, resource: { buffer: config.instanceAABBs } },
778
+ { binding: 1, resource: { buffer: config.instanceCount } },
779
+ { binding: 2, resource: { buffer: config.tlas.sceneBounds } },
780
+ { binding: 3, resource: { buffer: config.tlas.entityIds } },
781
+ ],
782
+ }),
783
+ morton: device.createBindGroup({
784
+ layout: pipelines.morton.getBindGroupLayout(0),
785
+ entries: [
786
+ { binding: 0, resource: { buffer: config.instanceAABBs } },
787
+ { binding: 1, resource: { buffer: config.instanceCount } },
788
+ { binding: 2, resource: { buffer: config.tlas.sceneBounds } },
789
+ { binding: 3, resource: { buffer: config.tlas.mortonCodes } },
790
+ { binding: 4, resource: { buffer: config.tlas.instanceIds } },
791
+ { binding: 5, resource: { buffer: config.tlas.entityIds } },
792
+ ],
793
+ }),
794
+ tree: device.createBindGroup({
795
+ layout: pipelines.tree.getBindGroupLayout(0),
796
+ entries: [
797
+ { binding: 0, resource: { buffer: config.tlas.mortonCodes } },
798
+ { binding: 1, resource: { buffer: config.instanceCount } },
799
+ { binding: 2, resource: { buffer: config.tlas.treeNodes } },
800
+ { binding: 3, resource: { buffer: config.tlas.parentIndices } },
801
+ ],
802
+ }),
803
+ propagate: device.createBindGroup({
804
+ layout: pipelines.propagate.getBindGroupLayout(0),
805
+ entries: [
806
+ { binding: 0, resource: { buffer: config.instanceAABBs } },
807
+ { binding: 1, resource: { buffer: config.tlas.instanceIds } },
808
+ { binding: 2, resource: { buffer: config.instanceCount } },
809
+ { binding: 3, resource: { buffer: config.tlas.treeNodes } },
810
+ { binding: 4, resource: { buffer: config.tlas.boundsFlags } },
811
+ { binding: 5, resource: { buffer: config.tlas.parentIndices } },
812
+ ],
813
+ }),
814
+ collapse: device.createBindGroup({
815
+ layout: pipelines.collapse.getBindGroupLayout(0),
816
+ entries: [
817
+ { binding: 0, resource: { buffer: config.tlas.treeNodes } },
818
+ { binding: 1, resource: { buffer: config.instanceCount } },
819
+ { binding: 2, resource: { buffer: config.tlas.parentIndices } },
820
+ { binding: 3, resource: { buffer: config.tlas.bvhNodes } },
821
+ ],
822
+ }),
823
+ };
824
+
825
+ return { pipelines, bindGroups, radixSort };
826
+ }
827
+
828
+ const initBounds = new Int32Array([
829
+ 0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0, 0x80800000, 0x80800000, 0x80800000, 0,
830
+ ]);
831
+
832
+ function executeTLAS(gpu: TLASGPU, ctx: ExecutionContext, config: TLASConfig): void {
833
+ const { device, encoder } = ctx;
834
+ const workgroups = Math.ceil(MAX_ENTITIES / WORKGROUP_SIZE);
835
+
836
+ device.queue.writeBuffer(config.tlas.sceneBounds, 0, initBounds);
837
+ encoder.clearBuffer(config.tlas.boundsFlags);
838
+ encoder.clearBuffer(config.tlas.parentIndices);
839
+
840
+ const boundsPass = encoder.beginComputePass();
841
+ boundsPass.setPipeline(gpu.pipelines.bounds);
842
+ boundsPass.setBindGroup(0, gpu.bindGroups.bounds);
843
+ boundsPass.dispatchWorkgroups(workgroups);
844
+ boundsPass.end();
845
+
846
+ const mortonPass = encoder.beginComputePass();
847
+ mortonPass.setPipeline(gpu.pipelines.morton);
848
+ mortonPass.setBindGroup(0, gpu.bindGroups.morton);
849
+ mortonPass.dispatchWorkgroups(workgroups);
850
+ mortonPass.end();
851
+
852
+ const sortPass = encoder.beginComputePass();
853
+ dispatchRadixSort(gpu.radixSort, sortPass);
854
+ sortPass.end();
855
+
856
+ const treePass = encoder.beginComputePass();
857
+ treePass.setPipeline(gpu.pipelines.tree);
858
+ treePass.setBindGroup(0, gpu.bindGroups.tree);
859
+ treePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
860
+ treePass.end();
861
+
862
+ const propagatePass = encoder.beginComputePass();
863
+ propagatePass.setPipeline(gpu.pipelines.propagate);
864
+ propagatePass.setBindGroup(0, gpu.bindGroups.propagate);
865
+ propagatePass.dispatchWorkgroups(workgroups);
866
+ propagatePass.end();
867
+
868
+ const collapsePass = encoder.beginComputePass();
869
+ collapsePass.setPipeline(gpu.pipelines.collapse);
870
+ collapsePass.setBindGroup(0, gpu.bindGroups.collapse);
871
+ collapsePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
872
+ collapsePass.end();
724
873
  }
725
874
 
726
875
  export function createTLASNode(config: TLASConfig): ComputeNode {
727
- let pipelines: TLASPipelines | null = null;
728
- let bindGroups: TLASBindGroups | null = null;
729
- let radixSort: Awaited<ReturnType<typeof createRadixSort>> | null = null;
876
+ let gpu: TLASGPU | null = null;
730
877
 
731
878
  return {
732
879
  id: "tlas",
@@ -742,145 +889,13 @@ export function createTLASNode(config: TLASConfig): ComputeNode {
742
889
  ],
743
890
 
744
891
  async prepare(device: GPUDevice) {
745
- const [boundsModule, mortonModule, treeModule, propagateModule, collapseModule] =
746
- await Promise.all([
747
- device.createShaderModule({ code: boundsShader }),
748
- device.createShaderModule({ code: mortonShader }),
749
- device.createShaderModule({ code: treeShader }),
750
- device.createShaderModule({ code: propagateShader }),
751
- device.createShaderModule({ code: collapseShader }),
752
- ]);
753
-
754
- const [bounds, morton, tree, propagate, collapse, sort] = await Promise.all([
755
- device.createComputePipelineAsync({
756
- layout: "auto",
757
- compute: { module: boundsModule, entryPoint: "main" },
758
- }),
759
- device.createComputePipelineAsync({
760
- layout: "auto",
761
- compute: { module: mortonModule, entryPoint: "main" },
762
- }),
763
- device.createComputePipelineAsync({
764
- layout: "auto",
765
- compute: { module: treeModule, entryPoint: "main" },
766
- }),
767
- device.createComputePipelineAsync({
768
- layout: "auto",
769
- compute: { module: propagateModule, entryPoint: "main" },
770
- }),
771
- device.createComputePipelineAsync({
772
- layout: "auto",
773
- compute: { module: collapseModule, entryPoint: "main" },
774
- }),
775
- createRadixSort(device, {
776
- keys: config.tlas.mortonCodes,
777
- values: config.tlas.instanceIds,
778
- count: MAX_ENTITIES,
779
- }),
780
- ]);
781
-
782
- pipelines = { bounds, morton, tree, propagate, collapse };
783
- radixSort = sort;
784
-
785
- bindGroups = {
786
- bounds: device.createBindGroup({
787
- layout: pipelines.bounds.getBindGroupLayout(0),
788
- entries: [
789
- { binding: 0, resource: { buffer: config.instanceAABBs } },
790
- { binding: 1, resource: { buffer: config.instanceCount } },
791
- { binding: 2, resource: { buffer: config.tlas.sceneBounds } },
792
- { binding: 3, resource: { buffer: config.tlas.entityIds } },
793
- ],
794
- }),
795
- morton: device.createBindGroup({
796
- layout: pipelines.morton.getBindGroupLayout(0),
797
- entries: [
798
- { binding: 0, resource: { buffer: config.instanceAABBs } },
799
- { binding: 1, resource: { buffer: config.instanceCount } },
800
- { binding: 2, resource: { buffer: config.tlas.sceneBounds } },
801
- { binding: 3, resource: { buffer: config.tlas.mortonCodes } },
802
- { binding: 4, resource: { buffer: config.tlas.instanceIds } },
803
- { binding: 5, resource: { buffer: config.tlas.entityIds } },
804
- ],
805
- }),
806
- tree: device.createBindGroup({
807
- layout: pipelines.tree.getBindGroupLayout(0),
808
- entries: [
809
- { binding: 0, resource: { buffer: config.tlas.mortonCodes } },
810
- { binding: 1, resource: { buffer: config.instanceCount } },
811
- { binding: 2, resource: { buffer: config.tlas.treeNodes } },
812
- { binding: 3, resource: { buffer: config.tlas.parentIndices } },
813
- ],
814
- }),
815
- propagate: device.createBindGroup({
816
- layout: pipelines.propagate.getBindGroupLayout(0),
817
- entries: [
818
- { binding: 0, resource: { buffer: config.instanceAABBs } },
819
- { binding: 1, resource: { buffer: config.tlas.instanceIds } },
820
- { binding: 2, resource: { buffer: config.instanceCount } },
821
- { binding: 3, resource: { buffer: config.tlas.treeNodes } },
822
- { binding: 4, resource: { buffer: config.tlas.boundsFlags } },
823
- { binding: 5, resource: { buffer: config.tlas.parentIndices } },
824
- ],
825
- }),
826
- collapse: device.createBindGroup({
827
- layout: pipelines.collapse.getBindGroupLayout(0),
828
- entries: [
829
- { binding: 0, resource: { buffer: config.tlas.treeNodes } },
830
- { binding: 1, resource: { buffer: config.instanceCount } },
831
- { binding: 2, resource: { buffer: config.tlas.parentIndices } },
832
- { binding: 3, resource: { buffer: config.tlas.bvhNodes } },
833
- ],
834
- }),
835
- };
892
+ gpu = await prepareTLAS(device, config);
836
893
  },
837
894
 
838
895
  execute(ctx: ExecutionContext) {
839
- const { device, encoder } = ctx;
840
-
841
- const workgroups = Math.ceil(MAX_ENTITIES / WORKGROUP_SIZE);
842
-
843
- const initBounds = new Int32Array([
844
- 0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0, 0x80800000, 0x80800000, 0x80800000, 0,
845
- ]);
846
- device.queue.writeBuffer(config.tlas.sceneBounds, 0, initBounds);
847
-
848
- encoder.clearBuffer(config.tlas.boundsFlags);
849
- encoder.clearBuffer(config.tlas.parentIndices);
850
-
851
- const boundsPass = encoder.beginComputePass();
852
- boundsPass.setPipeline(pipelines!.bounds);
853
- boundsPass.setBindGroup(0, bindGroups!.bounds);
854
- boundsPass.dispatchWorkgroups(workgroups);
855
- boundsPass.end();
856
-
857
- const mortonPass = encoder.beginComputePass();
858
- mortonPass.setPipeline(pipelines!.morton);
859
- mortonPass.setBindGroup(0, bindGroups!.morton);
860
- mortonPass.dispatchWorkgroups(workgroups);
861
- mortonPass.end();
862
-
863
- const sortPass = encoder.beginComputePass();
864
- radixSort!.dispatch(sortPass);
865
- sortPass.end();
866
-
867
- const treePass = encoder.beginComputePass();
868
- treePass.setPipeline(pipelines!.tree);
869
- treePass.setBindGroup(0, bindGroups!.tree);
870
- treePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
871
- treePass.end();
872
-
873
- const propagatePass = encoder.beginComputePass();
874
- propagatePass.setPipeline(pipelines!.propagate);
875
- propagatePass.setBindGroup(0, bindGroups!.propagate);
876
- propagatePass.dispatchWorkgroups(workgroups);
877
- propagatePass.end();
878
-
879
- const collapsePass = encoder.beginComputePass();
880
- collapsePass.setPipeline(pipelines!.collapse);
881
- collapsePass.setBindGroup(0, bindGroups!.collapse);
882
- collapsePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
883
- collapsePass.end();
896
+ if (config.getRaytracing && !config.getRaytracing()) return;
897
+ if (!gpu) return;
898
+ executeTLAS(gpu, ctx, config);
884
899
  },
885
900
  };
886
901
  }
@@ -1,11 +1,6 @@
1
- import {
2
- BVH_NODE_STRUCT_WGSL,
3
- LEAF_FLAG_WGSL,
4
- BLAS_NODE_STRUCT_WGSL,
5
- BLAS_TRIANGLE_STRUCT_WGSL,
6
- } from "./structs";
7
-
8
- export const OCT_DECODE_WGSL = /* wgsl */ `
1
+ import { BVH_NODE_STRUCT_WGSL, LEAF_FLAG_WGSL, BLAS_TRIANGLE_STRUCT_WGSL } from "./structs";
2
+
3
+ const OCT_DECODE_WGSL = /* wgsl */ `
9
4
  fn octDecode(enc: u32) -> vec3<f32> {
10
5
  let x = f32(enc & 0xFFFFu) / 65535.0 * 2.0 - 1.0;
11
6
  let y = f32(enc >> 16u) / 65535.0 * 2.0 - 1.0;
@@ -32,15 +27,13 @@ ${OCT_DECODE_WGSL}
32
27
  `;
33
28
 
34
29
  export const TLAS_BLAS_STRUCTS = /* wgsl */ `
35
- ${BLAS_NODE_STRUCT_WGSL}
36
-
37
30
  ${BLAS_TRIANGLE_STRUCT_WGSL}
38
31
  `;
39
32
 
40
33
  export const TLAS_BLAS_BINDINGS = /* wgsl */ `
41
34
  @group(1) @binding(0) var<storage, read> tlasNodes: array<BVHNode>;
42
35
  @group(1) @binding(1) var<storage, read> tlasInstanceIds: array<u32>;
43
- @group(1) @binding(2) var<storage, read> blasNodes: array<BLASNode>;
36
+ @group(1) @binding(2) var<storage, read> blasNodes: array<BVHNode>;
44
37
  @group(1) @binding(3) var<storage, read> blasTriIds: array<u32>;
45
38
  @group(1) @binding(4) var<storage, read> blasTriangles: array<BLASTriangle>;
46
39
  @group(1) @binding(5) var<storage, read> blasMeta: array<u32>;
@@ -142,7 +135,7 @@ fn traceBLAS(
142
135
  nodeOffset: u32,
143
136
  triIdOffset: u32,
144
137
  triOffset: u32,
145
- triCount_: u32,
138
+ totalTriCount: u32,
146
139
  maxT: f32
147
140
  ) -> HitResult {
148
141
  var closest: HitResult;
@@ -154,17 +147,7 @@ fn traceBLAS(
154
147
  closest.normal = vec3(0.0, 1.0, 0.0);
155
148
  closest.worldPos = vec3(0.0);
156
149
 
157
- if (triCount_ == 0u) {
158
- return closest;
159
- }
160
-
161
- if (triCount_ == 1u) {
162
- let triIdx = blasTriIds[triIdOffset];
163
- let tri = blasTriangles[triOffset + triIdx];
164
- let hit = intersectBLASTriangle(ray, tri);
165
- if (hit.hit && hit.t < maxT) {
166
- return hit;
167
- }
150
+ if (totalTriCount == 0u) {
168
151
  return closest;
169
152
  }
170
153
 
@@ -177,7 +160,7 @@ fn traceBLAS(
177
160
  stackPtr++;
178
161
 
179
162
  var iterations = 0u;
180
- let maxIterations = min(triCount_ * 3u, 10000u);
163
+ let maxIterations = min(totalTriCount * 3u, 10000u);
181
164
 
182
165
  while (stackPtr > 0u && iterations < maxIterations) {
183
166
  iterations++;
@@ -185,50 +168,75 @@ fn traceBLAS(
185
168
  let localIdx = stack[stackPtr];
186
169
  let node = blasNodes[nodeOffset + localIdx];
187
170
 
188
- let leftChild = node.leftChild;
189
- let rightChild = node.rightChild;
171
+ var children: array<u32, 4>;
172
+ var dists: array<f32, 4>;
190
173
 
191
- if (leftChild != INVALID_NODE) {
192
- if (isLeaf(leftChild)) {
193
- let leafIdx = leafIndex(leftChild);
194
- let triIdx = blasTriIds[triIdOffset + leafIdx];
195
- let tri = blasTriangles[triOffset + triIdx];
196
- let hit = intersectBLASTriangle(ray, tri);
197
- if (hit.hit && hit.t < closest.t) {
198
- closest = hit;
199
- }
200
- } else {
201
- let leftNode = blasNodes[nodeOffset + leftChild];
202
- let leftMin = vec3(leftNode.minX, leftNode.minY, leftNode.minZ);
203
- let leftMax = vec3(leftNode.maxX, leftNode.maxY, leftNode.maxZ);
204
- let leftDist = intersectAABBDist(ray.origin, invDir, leftMin, leftMax);
205
-
206
- if (leftDist < closest.t && stackPtr < MAX_STACK_DEPTH) {
207
- stack[stackPtr] = leftChild;
208
- stackPtr++;
209
- }
174
+ children[0] = node.child0;
175
+ children[1] = node.child1;
176
+ children[2] = node.child2;
177
+ children[3] = node.child3;
178
+
179
+ dists[0] = select(
180
+ intersectAABBDist(ray.origin, invDir,
181
+ vec3(node.c0_minX, node.c0_minY, node.c0_minZ),
182
+ vec3(node.c0_maxX, node.c0_maxY, node.c0_maxZ)),
183
+ 1e30,
184
+ children[0] == INVALID_NODE
185
+ );
186
+ dists[1] = select(
187
+ intersectAABBDist(ray.origin, invDir,
188
+ vec3(node.c1_minX, node.c1_minY, node.c1_minZ),
189
+ vec3(node.c1_maxX, node.c1_maxY, node.c1_maxZ)),
190
+ 1e30,
191
+ children[1] == INVALID_NODE
192
+ );
193
+ dists[2] = select(
194
+ intersectAABBDist(ray.origin, invDir,
195
+ vec3(node.c2_minX, node.c2_minY, node.c2_minZ),
196
+ vec3(node.c2_maxX, node.c2_maxY, node.c2_maxZ)),
197
+ 1e30,
198
+ children[2] == INVALID_NODE
199
+ );
200
+ dists[3] = select(
201
+ intersectAABBDist(ray.origin, invDir,
202
+ vec3(node.c3_minX, node.c3_minY, node.c3_minZ),
203
+ vec3(node.c3_maxX, node.c3_maxY, node.c3_maxZ)),
204
+ 1e30,
205
+ children[3] == INVALID_NODE
206
+ );
207
+
208
+ for (var i = 1u; i < 4u; i++) {
209
+ let keyDist = dists[i];
210
+ let keyChild = children[i];
211
+ var j = i;
212
+ while (j > 0u && dists[j - 1u] > keyDist) {
213
+ dists[j] = dists[j - 1u];
214
+ children[j] = children[j - 1u];
215
+ j--;
210
216
  }
217
+ dists[j] = keyDist;
218
+ children[j] = keyChild;
211
219
  }
212
220
 
213
- if (rightChild != INVALID_NODE) {
214
- if (isLeaf(rightChild)) {
215
- let leafIdx = leafIndex(rightChild);
221
+ for (var i = 3i; i >= 0i; i--) {
222
+ let child = children[i];
223
+ let dist = dists[i];
224
+
225
+ if (child == INVALID_NODE || dist >= closest.t) {
226
+ continue;
227
+ }
228
+
229
+ if (isLeaf(child)) {
230
+ let leafIdx = leafIndex(child);
216
231
  let triIdx = blasTriIds[triIdOffset + leafIdx];
217
232
  let tri = blasTriangles[triOffset + triIdx];
218
233
  let hit = intersectBLASTriangle(ray, tri);
219
234
  if (hit.hit && hit.t < closest.t) {
220
235
  closest = hit;
221
236
  }
222
- } else {
223
- let rightNode = blasNodes[nodeOffset + rightChild];
224
- let rightMin = vec3(rightNode.minX, rightNode.minY, rightNode.minZ);
225
- let rightMax = vec3(rightNode.maxX, rightNode.maxY, rightNode.maxZ);
226
- let rightDist = intersectAABBDist(ray.origin, invDir, rightMin, rightMax);
227
-
228
- if (rightDist < closest.t && stackPtr < MAX_STACK_DEPTH) {
229
- stack[stackPtr] = rightChild;
230
- stackPtr++;
231
- }
237
+ } else if (stackPtr < MAX_STACK_DEPTH) {
238
+ stack[stackPtr] = child;
239
+ stackPtr++;
232
240
  }
233
241
  }
234
242
  }
@@ -334,9 +342,9 @@ fn trace(ray: Ray) -> HitResult {
334
342
  let nodeOffset = blasMeta[shapeId * 4u];
335
343
  let triIdOffset = blasMeta[shapeId * 4u + 1u];
336
344
  let triOffset = blasMeta[shapeId * 4u + 2u];
337
- let triCount_ = blasMeta[shapeId * 4u + 3u];
345
+ let totalTriCount = blasMeta[shapeId * 4u + 3u];
338
346
 
339
- if (triCount_ == 0u) {
347
+ if (totalTriCount == 0u) {
340
348
  continue;
341
349
  }
342
350
 
@@ -345,7 +353,7 @@ fn trace(ray: Ray) -> HitResult {
345
353
  objRay.origin = (invMatrix * vec4(ray.origin, 1.0)).xyz;
346
354
  objRay.direction = (invMatrix * vec4(ray.direction, 0.0)).xyz;
347
355
 
348
- let blasHit = traceBLAS(objRay, nodeOffset, triIdOffset, triOffset, triCount_, closest.t);
356
+ let blasHit = traceBLAS(objRay, nodeOffset, triIdOffset, triOffset, totalTriCount, closest.t);
349
357
 
350
358
  if (blasHit.hit && blasHit.t < closest.t) {
351
359
  closest = blasHit;
@@ -440,9 +448,9 @@ fn traceAnyHit(ray: Ray, tMax: f32) -> bool {
440
448
  let nodeOffset = blasMeta[shapeId * 4u];
441
449
  let triIdOffset = blasMeta[shapeId * 4u + 1u];
442
450
  let triOffset = blasMeta[shapeId * 4u + 2u];
443
- let triCount_ = blasMeta[shapeId * 4u + 3u];
451
+ let totalTriCount = blasMeta[shapeId * 4u + 3u];
444
452
 
445
- if (triCount_ == 0u) {
453
+ if (totalTriCount == 0u) {
446
454
  continue;
447
455
  }
448
456
 
@@ -451,7 +459,7 @@ fn traceAnyHit(ray: Ray, tMax: f32) -> bool {
451
459
  objRay.origin = (invMatrix * vec4(ray.origin, 1.0)).xyz;
452
460
  objRay.direction = (invMatrix * vec4(ray.direction, 0.0)).xyz;
453
461
 
454
- let blasHit = traceBLAS(objRay, nodeOffset, triIdOffset, triOffset, triCount_, tMax);
462
+ let blasHit = traceBLAS(objRay, nodeOffset, triIdOffset, triOffset, totalTriCount, tMax);
455
463
  if (blasHit.hit && blasHit.t < tMax) {
456
464
  return true;
457
465
  }
@@ -1,11 +1,11 @@
1
1
  import type { ComputeNode, ExecutionContext } from "../compute";
2
- import { SCENE_STRUCT_WGSL } from "./shaders";
2
+ import { SCENE_STRUCT_WGSL } from "../render/surface/structs";
3
3
 
4
4
  const depthConvertShader = /* wgsl */ `
5
5
  ${SCENE_STRUCT_WGSL}
6
6
 
7
7
  @group(0) @binding(0) var<uniform> scene: Scene;
8
- @group(0) @binding(1) var linearDepthTex: texture_2d<f32>;
8
+ @group(0) @binding(1) var depthTex: texture_2d<f32>;
9
9
 
10
10
  struct VertexOutput {
11
11
  @builtin(position) position: vec4<f32>,
@@ -29,7 +29,7 @@ fn vs(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
29
29
  @fragment
30
30
  fn fs(input: VertexOutput) -> @builtin(frag_depth) f32 {
31
31
  let coords = vec2<i32>(input.position.xy);
32
- let t = textureLoad(linearDepthTex, coords, 0).r;
32
+ let t = textureLoad(depthTex, coords, 0).r;
33
33
 
34
34
  let near = scene.near;
35
35
  let far = scene.far;
@@ -59,8 +59,8 @@ export function createDepthConvertNode(config: DepthConvertConfig): ComputeNode
59
59
 
60
60
  return {
61
61
  id: "depth-convert",
62
- inputs: [{ id: "linear-depth", access: "read" }],
63
- outputs: [{ id: "depth", access: "write" }],
62
+ inputs: [{ id: "depth", access: "read" }],
63
+ outputs: [{ id: "z", access: "write" }],
64
64
 
65
65
  async prepare(device: GPUDevice) {
66
66
  const module = device.createShaderModule({ code: depthConvertShader });
@@ -84,10 +84,10 @@ export function createDepthConvertNode(config: DepthConvertConfig): ComputeNode
84
84
 
85
85
  const { device, encoder } = ctx;
86
86
 
87
- const linearDepthView = ctx.getTextureView("linear-depth");
88
87
  const depthView = ctx.getTextureView("depth");
88
+ const zView = ctx.getTextureView("z");
89
89
 
90
- if (!linearDepthView || !depthView) {
90
+ if (!depthView || !zView) {
91
91
  return;
92
92
  }
93
93
 
@@ -95,14 +95,14 @@ export function createDepthConvertNode(config: DepthConvertConfig): ComputeNode
95
95
  layout: pipeline!.getBindGroupLayout(0),
96
96
  entries: [
97
97
  { binding: 0, resource: { buffer: config.scene } },
98
- { binding: 1, resource: linearDepthView },
98
+ { binding: 1, resource: depthView },
99
99
  ],
100
100
  });
101
101
 
102
102
  const pass = encoder.beginRenderPass({
103
103
  colorAttachments: [],
104
104
  depthStencilAttachment: {
105
- view: depthView,
105
+ view: zView,
106
106
  depthClearValue: 1.0,
107
107
  depthLoadOp: "clear",
108
108
  depthStoreOp: "store",