gkjohnson / three-mesh-bvh

A BVH implementation to speed up raycasting and enable spatial queries against three.js meshes.
https://gkjohnson.github.io/three-mesh-bvh/example/bundle/raycast.html
MIT License
2.37k stars 247 forks source link

Raycast missing the bottom edge of a face randomly, likely based on floating point rounding #684

Closed ToyboxZach closed 10 hours ago

ToyboxZach commented 2 days ago

Describe the bug I am getting some weird behavior when probably due to floating point precision when dealing with raycasts.

I am trying to investigate some three-bvh-csg issues and I'm getting into a weird case where my raycast is failing to hit a flat plane sometimes.

Code


const vertices = new Float32Array( [
    -1.3836679458618164, 
-4.480311870574951,
10,

3.9273109436035156,
5.897944450378418,
8,

-1.3836679458618164,
-4.480311870574951,
8,

] );

const baseGeometry = new BufferGeometry();

baseGeometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
const bvh =  new MeshBVH(baseGeometry)

const mesh = new Mesh(baseGeometry, materials);
scene.add(mesh);

const rayCaster = new Raycaster()
  const _ray = new Ray();
    _ray.direction.copy(new Vector3(-0.16852837373762, 0.9856968028990217 , 0));
  _ray.origin.copy(new Vector3(-0.028221702575683627, -2.5058889150619503, 8));
    rayCaster.ray = _ray

  bvh.raycastFirst(_ray, DoubleSide);
  console.log("FIRST",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)
  _ray.origin.copy(new Vector3(-0.028221702575683627, -2.5058889150619503- 0.001, 8));
  console.log("SECOND",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)
  _ray.origin.copy(new Vector3(-0.028221702575683627, -2.5058889150619503+ 0.001, 8));
  console.log("THIRD",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)

  bvh.raycastFirst(_ray, DoubleSide);
  console.log("FOURTH",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)
  _ray.origin.copy(new Vector3(-0.028221702575683627- 0.001, -2.5058889150619503, 8));
  console.log("FITH",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)
  _ray.origin.copy(new Vector3(-0.028221702575683627+ 0.005, -2.5058889150619503, 8));
  console.log("SIXTH",bvh.raycastFirst(_ray, DoubleSide)?.faceIndex)

Live example https://jsfiddle.net/8w0ukx4o/53/

Expected behavior I would expect all my casts to either succeed or all to fail, I'm not sure which, but moving the origin just a small delta from my initial cast can make it hit. Even in multiple directions

Screenshots

This is the line, so its not right on the edge of the face or anything:

image

My analysis of it might be off, but I hit this case pretty deep into an algorithm, and this should be hitting this face

gkjohnson commented 2 days ago

This is the reality of raycasting exactly on a shared edge of two triangles, I think. You'll see that three.js' raycaster.interectObject exhibits the same issue. A raycast here will either have to produce two hits for the same point or none, and the implementation three.js has chose returns none. In the case of duplicate points you could deduplicate them by checking if it's a point that lies on an identical edge but the "correct" thing to do isn't necessarily obvious, I don't think.

Either way this project aims to return the same results that three.js returns so you may want to raise this in the three.js repo, instead.

ToyboxZach commented 1 day ago

Hmm maybe there is some confusion about what it should be hitting.

Here is an updated fiddle: https://jsfiddle.net/ers18ca5/59/

I compare it to the ray caster from threejs to show that in this case it is different. I also rendered the line so you can see what it is hitting.

gkjohnson commented 1 day ago

The fiddle isn't comparing the same calculations. Three.js' Mesh class transforms the ray by the inverted world matrix (in this case an identity matrix) which will still have a small effect on the ray values even if the mesh has identity transform values. The acceleratedRaycast function in this project does the same.

Here's a cleaned up and fixed fiddle showing that if you multiply an identity matrix in before calling raycastFirst you get the same results.

https://jsfiddle.net/qdokr2wp/1/

ToyboxZach commented 14 hours ago

Ah, I see. I didn’t realize threejs was doing an extra calculation. It does seem there is a threejs bug.

I fixed my case by just doing two ray casts with slightly different numbers, but I do still hit one case that I think might be bvh related since I’m not at the edge of a face but I’m casting right through the object.

It’s in the middle of a bigger calculation but Im going to try to isolate it better and see where the problem arises and I’ll close this issue if its unrelated

https://github.com/mrdoob/three.js/issues/11449