File size: 4,489 Bytes
b797337 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | /** @import { BVHComputeData } from '../BVHComputeData.js' */
import { float } from 'three/tsl';
import { wgslTagFn } from '../nodes/WGSLTagFnNode.js';
import { rayStruct, rayIntersectionResultStruct, bvhNodeStruct, bvhNodeBoundsStruct } from '../tsl/structs.js';
import { intersectRayTriangle } from '../tsl/fns.js';
/**
* Builds the `raycastFirstHit` shapecast function: traverses the BVH front to back and returns the
* closest ray / triangle intersection.
*
* @private
* @param {BVHComputeData} bvhData
* @returns {Function} TSL function node for the TLAS traversal.
*/
export function getRaycastFirstHitFn( bvhData ) {
// these are proxy nodes, so they can be referenced before the storage buffers exist
const { index, attributes, transforms } = bvhData.storage;
const scratchRayScalar = float( 1.0 ).toVar( `bvh_rayScalar_${ Math.random().toString( 36 ).substring( 2, 7 ) }` );
return bvhData.getShapecastFn( {
name: 'bvh_RaycastFirstHit',
shapeStruct: rayStruct,
resultStruct: rayIntersectionResultStruct,
boundsOrderFn: wgslTagFn/* wgsl */`
fn getBoundsOrder( ray: ${ rayStruct }, splitAxis: u32, node: ${ bvhNodeStruct } ) -> bool {
return ray.direction[ splitAxis ] >= 0.0;
}
`,
intersectsBoundsFn: wgslTagFn/* wgsl */`
fn rayIntersectsBounds( ray: ${ rayStruct }, bounds: ${ bvhNodeBoundsStruct }, result: ptr<function, ${ rayIntersectionResultStruct }> ) -> u32 {
let boundsMin = vec3( bounds.min[0], bounds.min[1], bounds.min[2] );
let boundsMax = vec3( bounds.max[0], bounds.max[1], bounds.max[2] );
let invDir = 1.0 / ray.direction;
let tMinPlane = ( boundsMin - ray.origin ) * invDir;
let tMaxPlane = ( boundsMax - ray.origin ) * invDir;
let tMinHit = vec3f(
min( tMinPlane.x, tMaxPlane.x ),
min( tMinPlane.y, tMaxPlane.y ),
min( tMinPlane.z, tMaxPlane.z )
);
let tMaxHit = vec3f(
max( tMinPlane.x, tMaxPlane.x ),
max( tMinPlane.y, tMaxPlane.y ),
max( tMinPlane.z, tMaxPlane.z )
);
let t0 = max( max( tMinHit.x, tMinHit.y ), tMinHit.z );
let t1 = min( min( tMaxHit.x, tMaxHit.y ), tMaxHit.z );
let dist = max( t0, 0.0 );
if ( t1 < dist ) {
return 0u;
} else if ( ray.maxDist > 0.0 && dist * ${ scratchRayScalar } >= ray.maxDist ) {
return 0u;
} else if ( result.didHit && dist * ${ scratchRayScalar } >= result.dist ) {
return 0u;
} else {
return 1u;
}
}
`,
intersectRangeFn: wgslTagFn/* wgsl */`
fn intersectRange( ray: ${ rayStruct }, offset: u32, count: u32, result: ptr<function, ${ rayIntersectionResultStruct }> ) -> bool {
var didHit = false;
for ( var ti = offset; ti < offset + count; ti = ti + 1u ) {
let i0 = ${ index }[ ti * 3u ];
let i1 = ${ index }[ ti * 3u + 1u ];
let i2 = ${ index }[ ti * 3u + 2u ];
let a = ${ attributes }[ i0 ].position.xyz;
let b = ${ attributes }[ i1 ].position.xyz;
let c = ${ attributes }[ i2 ].position.xyz;
var triResult = ${ intersectRayTriangle }( ray, a, b, c, 0.0 );
triResult.dist *= ${ scratchRayScalar };
if ( triResult.didHit && ( ray.maxDist <= 0.0 || triResult.dist < ray.maxDist ) && ( ! result.didHit || triResult.dist < result.dist ) ) {
result.didHit = true;
result.dist = triResult.dist;
result.normal = triResult.normal;
result.side = triResult.side;
result.barycoord = triResult.barycoord;
result.indices = vec4u( i0, i1, i2, ti );
didHit = true;
}
}
return didHit;
}
`,
transformShapeFn: wgslTagFn/* wgsl */`
fn transformRay( ray: ptr<function, ${ rayStruct }>, objectIndex: u32 ) -> void {
let toLocal = ${ transforms }[ objectIndex ].inverseMatrixWorld;
ray.origin = ( toLocal * vec4f( ray.origin, 1.0 ) ).xyz;
ray.direction = ( toLocal * vec4f( ray.direction, 0.0 ) ).xyz;
let len = length( ray.direction );
ray.direction /= len;
${ scratchRayScalar } = 1.0 / len;
}
`,
transformResultFn: wgslTagFn/* wgsl */`
fn transformResult( hit: ptr<function, ${ rayIntersectionResultStruct }>, objectIndex: u32 ) -> void {
let toLocal = ${ transforms }[ objectIndex ].inverseMatrixWorld;
hit.normal = normalize( ( transpose( toLocal ) * vec4f( hit.normal, 0.0 ) ).xyz );
hit.objectIndex = objectIndex;
}
`,
resetShapeFn: wgslTagFn/* wgsl */`
fn resetRayScalar( objectIndex: u32 ) -> void {
${ scratchRayScalar } = 1.0;
}
`,
} );
}
|