UNPKG

three-mesh-bvh

Version:

A BVH implementation to speed up raycasting against three.js meshes.

176 lines (109 loc) 3.9 kB
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 ] );