@multiplekex/shallot 0.2.5 → 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.
- package/package.json +1 -1
- package/src/core/component.ts +1 -1
- package/src/core/index.ts +1 -13
- package/src/core/math.ts +186 -0
- package/src/core/state.ts +1 -1
- package/src/core/xml.ts +56 -41
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +10 -65
- package/src/extras/{water.ts → water/index.ts} +59 -4
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +146 -472
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/raytracing/bvh/blas.ts +335 -87
- package/src/standard/raytracing/bvh/radix.ts +225 -228
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/raytracing/bvh/structs.ts +0 -55
- package/src/standard/raytracing/bvh/tlas.ts +153 -141
- package/src/standard/raytracing/bvh/traverse.ts +72 -64
- package/src/standard/raytracing/index.ts +233 -204
- package/src/standard/raytracing/instance.ts +30 -18
- package/src/standard/raytracing/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +23 -40
- package/src/standard/render/camera.ts +10 -28
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +68 -12
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +5 -2
- package/src/standard/render/postprocess.ts +263 -267
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +265 -11
- package/src/standard/render/surface/structs.ts +10 -0
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -315
- package/src/standard/render/mesh/plane.ts +0 -11
- package/src/standard/render/mesh/sphere.ts +0 -40
- package/src/standard/render/mesh/unified.ts +0 -96
- package/src/standard/render/surface/compile.ts +0 -65
- package/src/standard/render/surface/noise.ts +0 -58
|
@@ -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,
|
|
@@ -724,10 +724,156 @@ export interface TLASConfig {
|
|
|
724
724
|
getRaytracing?: () => boolean;
|
|
725
725
|
}
|
|
726
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();
|
|
873
|
+
}
|
|
874
|
+
|
|
727
875
|
export function createTLASNode(config: TLASConfig): ComputeNode {
|
|
728
|
-
let
|
|
729
|
-
let bindGroups: TLASBindGroups | null = null;
|
|
730
|
-
let radixSort: Awaited<ReturnType<typeof createRadixSort>> | null = null;
|
|
876
|
+
let gpu: TLASGPU | null = null;
|
|
731
877
|
|
|
732
878
|
return {
|
|
733
879
|
id: "tlas",
|
|
@@ -743,147 +889,13 @@ export function createTLASNode(config: TLASConfig): ComputeNode {
|
|
|
743
889
|
],
|
|
744
890
|
|
|
745
891
|
async prepare(device: GPUDevice) {
|
|
746
|
-
|
|
747
|
-
await Promise.all([
|
|
748
|
-
device.createShaderModule({ code: boundsShader }),
|
|
749
|
-
device.createShaderModule({ code: mortonShader }),
|
|
750
|
-
device.createShaderModule({ code: treeShader }),
|
|
751
|
-
device.createShaderModule({ code: propagateShader }),
|
|
752
|
-
device.createShaderModule({ code: collapseShader }),
|
|
753
|
-
]);
|
|
754
|
-
|
|
755
|
-
const [bounds, morton, tree, propagate, collapse, sort] = await Promise.all([
|
|
756
|
-
device.createComputePipelineAsync({
|
|
757
|
-
layout: "auto",
|
|
758
|
-
compute: { module: boundsModule, entryPoint: "main" },
|
|
759
|
-
}),
|
|
760
|
-
device.createComputePipelineAsync({
|
|
761
|
-
layout: "auto",
|
|
762
|
-
compute: { module: mortonModule, entryPoint: "main" },
|
|
763
|
-
}),
|
|
764
|
-
device.createComputePipelineAsync({
|
|
765
|
-
layout: "auto",
|
|
766
|
-
compute: { module: treeModule, entryPoint: "main" },
|
|
767
|
-
}),
|
|
768
|
-
device.createComputePipelineAsync({
|
|
769
|
-
layout: "auto",
|
|
770
|
-
compute: { module: propagateModule, entryPoint: "main" },
|
|
771
|
-
}),
|
|
772
|
-
device.createComputePipelineAsync({
|
|
773
|
-
layout: "auto",
|
|
774
|
-
compute: { module: collapseModule, entryPoint: "main" },
|
|
775
|
-
}),
|
|
776
|
-
createRadixSort(device, {
|
|
777
|
-
keys: config.tlas.mortonCodes,
|
|
778
|
-
values: config.tlas.instanceIds,
|
|
779
|
-
count: MAX_ENTITIES,
|
|
780
|
-
}),
|
|
781
|
-
]);
|
|
782
|
-
|
|
783
|
-
pipelines = { bounds, morton, tree, propagate, collapse };
|
|
784
|
-
radixSort = sort;
|
|
785
|
-
|
|
786
|
-
bindGroups = {
|
|
787
|
-
bounds: device.createBindGroup({
|
|
788
|
-
layout: pipelines.bounds.getBindGroupLayout(0),
|
|
789
|
-
entries: [
|
|
790
|
-
{ binding: 0, resource: { buffer: config.instanceAABBs } },
|
|
791
|
-
{ binding: 1, resource: { buffer: config.instanceCount } },
|
|
792
|
-
{ binding: 2, resource: { buffer: config.tlas.sceneBounds } },
|
|
793
|
-
{ binding: 3, resource: { buffer: config.tlas.entityIds } },
|
|
794
|
-
],
|
|
795
|
-
}),
|
|
796
|
-
morton: device.createBindGroup({
|
|
797
|
-
layout: pipelines.morton.getBindGroupLayout(0),
|
|
798
|
-
entries: [
|
|
799
|
-
{ binding: 0, resource: { buffer: config.instanceAABBs } },
|
|
800
|
-
{ binding: 1, resource: { buffer: config.instanceCount } },
|
|
801
|
-
{ binding: 2, resource: { buffer: config.tlas.sceneBounds } },
|
|
802
|
-
{ binding: 3, resource: { buffer: config.tlas.mortonCodes } },
|
|
803
|
-
{ binding: 4, resource: { buffer: config.tlas.instanceIds } },
|
|
804
|
-
{ binding: 5, resource: { buffer: config.tlas.entityIds } },
|
|
805
|
-
],
|
|
806
|
-
}),
|
|
807
|
-
tree: device.createBindGroup({
|
|
808
|
-
layout: pipelines.tree.getBindGroupLayout(0),
|
|
809
|
-
entries: [
|
|
810
|
-
{ binding: 0, resource: { buffer: config.tlas.mortonCodes } },
|
|
811
|
-
{ binding: 1, resource: { buffer: config.instanceCount } },
|
|
812
|
-
{ binding: 2, resource: { buffer: config.tlas.treeNodes } },
|
|
813
|
-
{ binding: 3, resource: { buffer: config.tlas.parentIndices } },
|
|
814
|
-
],
|
|
815
|
-
}),
|
|
816
|
-
propagate: device.createBindGroup({
|
|
817
|
-
layout: pipelines.propagate.getBindGroupLayout(0),
|
|
818
|
-
entries: [
|
|
819
|
-
{ binding: 0, resource: { buffer: config.instanceAABBs } },
|
|
820
|
-
{ binding: 1, resource: { buffer: config.tlas.instanceIds } },
|
|
821
|
-
{ binding: 2, resource: { buffer: config.instanceCount } },
|
|
822
|
-
{ binding: 3, resource: { buffer: config.tlas.treeNodes } },
|
|
823
|
-
{ binding: 4, resource: { buffer: config.tlas.boundsFlags } },
|
|
824
|
-
{ binding: 5, resource: { buffer: config.tlas.parentIndices } },
|
|
825
|
-
],
|
|
826
|
-
}),
|
|
827
|
-
collapse: device.createBindGroup({
|
|
828
|
-
layout: pipelines.collapse.getBindGroupLayout(0),
|
|
829
|
-
entries: [
|
|
830
|
-
{ binding: 0, resource: { buffer: config.tlas.treeNodes } },
|
|
831
|
-
{ binding: 1, resource: { buffer: config.instanceCount } },
|
|
832
|
-
{ binding: 2, resource: { buffer: config.tlas.parentIndices } },
|
|
833
|
-
{ binding: 3, resource: { buffer: config.tlas.bvhNodes } },
|
|
834
|
-
],
|
|
835
|
-
}),
|
|
836
|
-
};
|
|
892
|
+
gpu = await prepareTLAS(device, config);
|
|
837
893
|
},
|
|
838
894
|
|
|
839
895
|
execute(ctx: ExecutionContext) {
|
|
840
896
|
if (config.getRaytracing && !config.getRaytracing()) return;
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const workgroups = Math.ceil(MAX_ENTITIES / WORKGROUP_SIZE);
|
|
845
|
-
|
|
846
|
-
const initBounds = new Int32Array([
|
|
847
|
-
0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0, 0x80800000, 0x80800000, 0x80800000, 0,
|
|
848
|
-
]);
|
|
849
|
-
device.queue.writeBuffer(config.tlas.sceneBounds, 0, initBounds);
|
|
850
|
-
|
|
851
|
-
encoder.clearBuffer(config.tlas.boundsFlags);
|
|
852
|
-
encoder.clearBuffer(config.tlas.parentIndices);
|
|
853
|
-
|
|
854
|
-
const boundsPass = encoder.beginComputePass();
|
|
855
|
-
boundsPass.setPipeline(pipelines!.bounds);
|
|
856
|
-
boundsPass.setBindGroup(0, bindGroups!.bounds);
|
|
857
|
-
boundsPass.dispatchWorkgroups(workgroups);
|
|
858
|
-
boundsPass.end();
|
|
859
|
-
|
|
860
|
-
const mortonPass = encoder.beginComputePass();
|
|
861
|
-
mortonPass.setPipeline(pipelines!.morton);
|
|
862
|
-
mortonPass.setBindGroup(0, bindGroups!.morton);
|
|
863
|
-
mortonPass.dispatchWorkgroups(workgroups);
|
|
864
|
-
mortonPass.end();
|
|
865
|
-
|
|
866
|
-
const sortPass = encoder.beginComputePass();
|
|
867
|
-
radixSort!.dispatch(sortPass);
|
|
868
|
-
sortPass.end();
|
|
869
|
-
|
|
870
|
-
const treePass = encoder.beginComputePass();
|
|
871
|
-
treePass.setPipeline(pipelines!.tree);
|
|
872
|
-
treePass.setBindGroup(0, bindGroups!.tree);
|
|
873
|
-
treePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
|
|
874
|
-
treePass.end();
|
|
875
|
-
|
|
876
|
-
const propagatePass = encoder.beginComputePass();
|
|
877
|
-
propagatePass.setPipeline(pipelines!.propagate);
|
|
878
|
-
propagatePass.setBindGroup(0, bindGroups!.propagate);
|
|
879
|
-
propagatePass.dispatchWorkgroups(workgroups);
|
|
880
|
-
propagatePass.end();
|
|
881
|
-
|
|
882
|
-
const collapsePass = encoder.beginComputePass();
|
|
883
|
-
collapsePass.setPipeline(pipelines!.collapse);
|
|
884
|
-
collapsePass.setBindGroup(0, bindGroups!.collapse);
|
|
885
|
-
collapsePass.dispatchWorkgroups(Math.ceil((MAX_ENTITIES - 1) / WORKGROUP_SIZE));
|
|
886
|
-
collapsePass.end();
|
|
897
|
+
if (!gpu) return;
|
|
898
|
+
executeTLAS(gpu, ctx, config);
|
|
887
899
|
},
|
|
888
900
|
};
|
|
889
901
|
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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<
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
189
|
-
|
|
171
|
+
var children: array<u32, 4>;
|
|
172
|
+
var dists: array<f32, 4>;
|
|
190
173
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
224
|
-
|
|
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
|
|
345
|
+
let totalTriCount = blasMeta[shapeId * 4u + 3u];
|
|
338
346
|
|
|
339
|
-
if (
|
|
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,
|
|
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
|
|
451
|
+
let totalTriCount = blasMeta[shapeId * 4u + 3u];
|
|
444
452
|
|
|
445
|
-
if (
|
|
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,
|
|
462
|
+
let blasHit = traceBLAS(objRay, nodeOffset, triIdOffset, triOffset, totalTriCount, tMax);
|
|
455
463
|
if (blasHit.hit && blasHit.t < tMax) {
|
|
456
464
|
return true;
|
|
457
465
|
}
|