three-mesh-bvh
Version:
A BVH implementation to speed up raycasting against three.js meshes.
176 lines (109 loc) • 3.9 kB
JavaScript
import { wgslFn } from 'three/tsl';
import { bvhNodeStruct, intersectionResultStruct, intersectsBounds, rayStruct, constants } from './common_functions.wgsl.js';
export const intersectsTriangle = wgslFn( /* wgsl */ `
fn intersectsTriangle( ray: Ray, a: vec3f, b: vec3f, c: vec3f ) -> IntersectionResult {
var result: IntersectionResult;
result.didHit = false;
let edge1 = b - a;
let edge2 = c - a;
let n = cross( edge1, edge2 );
let det = - dot( ray.direction, n );
if ( abs( det ) < TRI_INTERSECT_EPSILON ) {
return result;
}
let invdet = 1.0 / det;
let AO = ray.origin - a;
let DAO = cross( AO, ray.direction );
let u = dot( edge2, DAO ) * invdet;
let v = -dot( edge1, DAO ) * invdet;
let t = dot( AO, n ) * invdet;
let w = 1.0 - u - v;
if ( u < - TRI_INTERSECT_EPSILON || v < - TRI_INTERSECT_EPSILON || w < - TRI_INTERSECT_EPSILON || t < TRI_INTERSECT_EPSILON ) {
return result;
}
result.didHit = true;
result.barycoord = vec3f( w, u, v );
result.dist = t;
result.side = sign( det );
result.normal = result.side * normalize( n );
return result;
}
`, [ rayStruct, intersectionResultStruct, constants ] );
export const intersectTriangles = wgslFn( /* wgsl */ `
fn intersectTriangles(
bvh_position: ptr<storage, array<vec3f>, read>,
bvh_index: ptr<storage, array<vec3u>, read>,
offset: u32,
count: u32,
ray: Ray
) -> IntersectionResult {
var closestResult: IntersectionResult;
closestResult.didHit = false;
closestResult.dist = INFINITY;
for ( var i = offset; i < offset + count; i = i + 1u ) {
let indices = bvh_index[ i ];
let a = bvh_position[ indices.x ];
let b = bvh_position[ indices.y ];
let c = bvh_position[ indices.z ];
var triResult = intersectsTriangle( ray, a, b, c );
if ( triResult.didHit && triResult.dist < closestResult.dist ) {
closestResult = triResult;
closestResult.indices = vec4u( indices.xyz, i );
}
}
return closestResult;
}
`, [ intersectsTriangle, rayStruct, intersectionResultStruct, constants ] );
export const bvhIntersectFirstHit = wgslFn( /* wgsl */ `
fn bvhIntersectFirstHit(
bvh_index: ptr<storage, array<vec3u>, read>,
bvh_position: ptr<storage, array<vec3f>, read>,
bvh: ptr<storage, array<BVHNode>,read>,
ray: Ray,
) -> IntersectionResult {
var pointer = 0;
var stack: array<u32, BVH_STACK_DEPTH>;
stack[ 0 ] = 0u;
var bestHit: IntersectionResult;
bestHit.didHit = false;
bestHit.dist = INFINITY;
loop {
if ( pointer < 0 || pointer >= i32( BVH_STACK_DEPTH ) ) {
break;
}
let currNodeIndex = stack[ pointer ];
let node = bvh[ currNodeIndex ];
pointer = pointer - 1;
var boundsHitDistance: f32 = 0.0;
if ( ! intersectsBounds( ray, node.bounds, &boundsHitDistance ) || boundsHitDistance > bestHit.dist ) {
continue;
}
let boundsInfox = node.splitAxisOrTriangleCount;
let boundsInfoy = node.rightChildOrTriangleOffset;
let isLeaf = ( boundsInfox & 0xffff0000u ) != 0u;
if ( isLeaf ) {
let count = boundsInfox & 0x0000ffffu;
let offset = boundsInfoy;
let localHit = intersectTriangles(
bvh_position, bvh_index, offset,
count, ray
);
if ( localHit.didHit && localHit.dist < bestHit.dist ) {
bestHit = localHit;
}
} else {
let leftIndex = currNodeIndex + 1u;
let splitAxis = boundsInfox & 0x0000ffffu;
let rightIndex = currNodeIndex + boundsInfoy;
let leftToRight = ray.direction[splitAxis] >= 0.0;
let c1 = select( rightIndex, leftIndex, leftToRight );
let c2 = select( leftIndex, rightIndex, leftToRight );
pointer = pointer + 1;
stack[ pointer ] = c2;
pointer = pointer + 1;
stack[ pointer ] = c1;
}
}
return bestHit;
}
`, [ intersectTriangles, intersectsBounds, rayStruct, bvhNodeStruct, intersectionResultStruct, constants ] );