Setting the file. One moment.
GPU Sdf · Frost Sequence Camera Orbit · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Three Mesh BVH LICENSE
(opens in a new tab)
source/src/shape/ gpuSdf.ts
TypeScript · 136 lines · 5 KB
const
{
Fn
,
instanceIndex
,
uint
,
vec3
,
float
,
If
,
dot
,
max
,
instancedArray
,
uniform
}
=
tsl;
8
9 /** Runtime GPU closest-point queries. SVG/text geometry remains fully editable.
10 * CPU winding uses the exact existing column algorithm; the simulation API is unchanged. */
11 export async function voxelizeGPU (
12 renderer : THREE . WebGPURenderer ,
13 geo : THREE . BufferGeometry ,
14 bound : number ,
15 res : number ,
16 sampleBound : number ,
17 ) {
18 const start = performance. now (),
19 half = halfExtents (geo),
20 thickness = Math. min (half.x, half.y, half.z),
21 n = Math. max ( 16 , Math. round (res));
22 const range = bound,
23 voxel = ( 2 * sampleBound) / n,
24 maxDist = Math. min (range, Math. max (thickness * 2.5 , voxel * 14 ));
25 const bvh = new MeshBVH (geo);
26 (geo as any ).boundsTree = bvh;
27 const data = new BVHComputeData (geo);
28 data. update ();
29 const packed = performance. now ();
30 const query = wgslTagFn `
31 fn frostClosest(p:vec3f,cap:f32)->f32 {
32 var hit:${ pointQueryResultStruct };
33 hit.found=true; hit.distanceSq=cap*cap;
34 let found=${ data . fns . closestPointToPoint }(p,&hit);
35 return select(-1.0,sqrt(hit.distanceSq),found);
36 }` ;
37 const output = instancedArray (n * n * n, "float" ),
38 offset = uniform ( 0 , "uint" );
39 const box = geo.boundingBox ! ,
40 lo = vec3 (box.min.x, box.min.y, box.min.z),
41 hi = vec3 (box.max.x, box.max.y, box.max.z);
42 const slabs = 8 ,
43 slabCount = n * n * slabs;
44 const kernel = Fn (() => {
45 const i = instanceIndex. add (offset),
46 x = i. mod ( uint (n)),
47 y = i. div ( uint (n)). mod ( uint (n)),
48 z = i. div ( uint (n * n));
49 const p = vec3 (x, y, z)
50 . add ( 0.5 )
51 . div (n)
52 . sub ( 0.5 )
53 . mul ( 2 * sampleBound)
54 . toVar ();
55 const delta = max ( max (lo. sub (p), p. sub (hi)), vec3 ( 0 ));
56 const d = float ( - 1 ). toVar ();
57 If ( dot (delta, delta). lessThanEqual (maxDist * maxDist), () => {
58 d. assign ( query (p, float (maxDist)));
59 });
60 output. element (i). assign (d);
61 })(). compute (slabCount, [ 64 ]);
62 for ( let base = 0 ; base < n * n * n; base += slabCount) {
63 offset.value = base;
64 renderer. compute (kernel);
65 await renderer.backend.device.queue. onSubmittedWorkDone ();
66 }
67 const distances = new Float32Array ( await renderer. getArrayBufferAsync (output.value));
68 const gpuDone = performance. now ();
69 // Audit sampled GPU distances against the original CPU mesh query.
70 let auditMax = 0 ,
71 auditSum = 0 ,
72 auditCount = 0 ,
73 auditMissing = 0 ,
74 random = 173 ;
75 const auditP = new THREE . Vector3 ();
76 for ( let a = 0 ; a < 2048 ; a ++ ) {
77 random = (Math. imul (random, 1664525 ) + 1013904223 ) >>> 0 ;
78 const i = random % (n * n * n),
79 x = i % n,
80 y = Math. floor (i / n) % n,
81 z = Math. floor (i / (n * n));
82 auditP. set (
83 ((x + 0.5 ) / n - 0.5 ) * 2 * sampleBound,
84 ((y + 0.5 ) / n - 0.5 ) * 2 * sampleBound,
85 ((z + 0.5 ) / n - 0.5 ) * 2 * sampleBound,
86 );
87 const hit = bvh. closestPointToPoint (auditP, {}, 0 , maxDist);
88 if (hit && hit.distance <= maxDist && distances[i] >= 0 ) {
89 const e = Math. abs (hit.distance - distances[i]);
90 auditMax = Math. max (auditMax, e);
91 auditSum += e * e;
92 auditCount ++ ;
93 } else if ( !! (hit && hit.distance <= maxDist) !== distances[i] >= 0 ) auditMissing ++ ;
94 }
95 // Match the original nonzero winding rule, including deterministic column nudges.
96 const ray = new THREE . Ray ( new THREE . Vector3 (), new THREE . Vector3 ( 0 , 0 , 1 ));
97 for ( let y = 0 ; y < n; y ++ )
98 for ( let x = 0 ; x < n; x ++ ) {
99 const cx = ((x + 0.5 ) / n - 0.5 ) * 2 * sampleBound + voxel * 0.013 ,
100 cy = ((y + 0.5 ) / n - 0.5 ) * 2 * sampleBound + voxel * 0.017 ;
101 ray.origin. set (cx, cy, - sampleBound - 1 );
102 const crossings = (bvh. raycast (ray, THREE .DoubleSide) as any [])
103 . map (( h ) => ({ z: h.distance - sampleBound - 1 , delta: h.face.normal.z < 0 ? 1 : - 1 }))
104 . sort (( a , b ) => a.z - b.z);
105 let k = 0 ,
106 winding = 0 ;
107 for ( let z = 0 ; z < n; z ++ ) {
108 const pz = ((z + 0.5 ) / n - 0.5 ) * 2 * sampleBound;
109 while (k < crossings. length && crossings[k].z < pz) winding += crossings[k ++ ].delta;
110 const index = x + y * n + z * n * n;
111 let d = distances[index] >= 0 ? distances[index] : range;
112 if (winding !== 0 ) d = - d;
113 distances[index] = Math. min ( 1 , Math. max ( 0 , (d + range) / ( 2 * range)));
114 }
115 }
116 const finished = performance. now ();
117 rwMetrics.sdf. push ({
118 n,
119 triangles: geo.index ! .count / 3 ,
120 packMs: packed - start,
121 gpuMs: gpuDone - packed,
122 signMs: finished - gpuDone,
123 totalMs: finished - start,
124 audit: {
125 samples: 2048 ,
126 compared: auditCount,
127 maxDistanceError: auditMax,
128 rmsDistanceError: Math. sqrt (auditSum / Math. max ( 1 , auditCount)),
129 missing: auditMissing,
130 },
131 });
132 data. dispose ();
133 output.value. dispose ();
134 kernel. dispose ();
135 return makeLogoSDF (distances, n, bound, thickness, sampleBound);
136 }