@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.
- 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/arrows/index.ts +3 -3
- package/src/extras/caustic.ts +37 -0
- package/src/extras/gradient/index.ts +63 -69
- package/src/extras/index.ts +3 -0
- package/src/extras/lines/index.ts +3 -3
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/skylab/index.ts +314 -0
- package/src/extras/text/font.ts +69 -14
- package/src/extras/text/index.ts +17 -69
- package/src/extras/text/sdf.ts +13 -2
- package/src/extras/water/index.ts +119 -0
- package/src/standard/defaults.ts +2 -0
- package/src/standard/index.ts +2 -0
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +191 -0
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/{render → raytracing}/bvh/blas.ts +336 -88
- package/src/standard/raytracing/bvh/radix.ts +473 -0
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/{render → raytracing}/bvh/structs.ts +0 -55
- package/src/standard/{render → raytracing}/bvh/tlas.ts +155 -140
- package/src/standard/{render → raytracing}/bvh/traverse.ts +72 -64
- package/src/standard/{render → raytracing}/depth.ts +9 -9
- package/src/standard/raytracing/index.ts +409 -0
- package/src/standard/{render → raytracing}/instance.ts +31 -16
- package/src/standard/{render → raytracing}/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +798 -0
- package/src/standard/{render → raytracing}/triangle.ts +1 -1
- package/src/standard/render/camera.ts +96 -106
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +136 -220
- package/src/standard/render/indirect.ts +9 -10
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +8 -5
- package/src/standard/render/pass.ts +1 -1
- package/src/standard/render/postprocess.ts +263 -242
- package/src/standard/render/scene.ts +28 -16
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +511 -0
- package/src/standard/render/surface/structs.ts +23 -6
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/bvh/radix.ts +0 -476
- package/src/standard/render/forward/index.ts +0 -259
- package/src/standard/render/forward/raster.ts +0 -228
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -446
- 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/shaders.ts +0 -484
- package/src/standard/render/surface/compile.ts +0 -67
- package/src/standard/render/surface/noise.ts +0 -45
- package/src/standard/render/surface/wgsl.ts +0 -573
- /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
|
|
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
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
2
|
-
import { SCENE_STRUCT_WGSL } from "
|
|
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
|
|
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(
|
|
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: "
|
|
63
|
-
outputs: [{ id: "
|
|
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 (!
|
|
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:
|
|
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:
|
|
105
|
+
view: zView,
|
|
106
106
|
depthClearValue: 1.0,
|
|
107
107
|
depthLoadOp: "clear",
|
|
108
108
|
depthStoreOp: "store",
|