R3F RayCasting and BVH

황상진·2025년 7월 25일

Raycasting

배경

3D 객체간의 상호작용

참고
https://drei.docs.pmnd.rs/performances/bvh

Raycasting란?

3D 공간에서 Ray를 발사하여, 이 Ray가 어떤 3D object와 교차하는지 검사하는 컴퓨터 그래픽스 기술

Raycasting 작동 원리

  • Ray 생성
  • Intersection 검사
  • 충돌 지점 반환

R3F RayCasting

R3F에서는 Canvas 컴포넌트 내부에서 렌더링되는 모든 Three.js mesh object는 기본적으로 Raycasting 기능을 자동으로 가진다.

https://r3f.docs.pmnd.rs/api/events

  • 자동 Ray 생성 및 업데이트
  • Scene Graph 탐색 및 intersection 검사
  • pointer event 발생 및 전파

성능 최적화

편리하지만, 모든 Raycast 가능한 객체를 대상으로 Ray intersection 검사를 진행한다.

  • object가 많고
  • 복잡한 mesh로 구성된 경우
    계산량이 기하급수적으로 늘어남

BVH

위의 문제 상황을 해결하기 위해서 BVH를 사용

BVH란?

Bounding Volume Hierarchy

어떻게 하면 3D scene 상에서 Intersection 검사를 최소화할 수 있을까
object 하나하나 검사한다면 계산량이 늘어난다.

Bounding Volume

단순한 형태로 해당 object들을 감싸고 이를 검사하면 계산이 줄어들 것이다.

단순한 형태를 AABB로 구성해보자
Axis Aligned Bounding Box

Intersection 검사 과정에서
Bounding Box에 교차되지 않으면 절대로 실제 object와 교차되지 않으므로 검사를 진행하지 않는다.

Hierarchy

이러한 Bounding Volume을 계층적 Tree 구조로 관리하는 것이 BVH이다.

  • 여러개의 작은 Bounding Volume이 모여서 더 큰 Bounding volume을 형성

  • 다시 더 큰 Bounding volume이 모여서 보다 더 큰 Bounding volume을 형성하여 최종적으로 하나의 거대한 Bounding volume(root node)을 형성

  • 이렇게 트리구조로 구정하면 object와 교차되는 검사를 이분 탐색으로 진행할 수 있어서
    탐색 효율성이 올라간다.

R3F에 BVH 적용방법

두가지 방법이 있다.

  • @react-three/bvh
  • three-mesh-bvh

@react-three/bvh

https://drei.docs.pmnd.rs/performances/bvh

  • 설치
npm install @react-three/bvh three-mesh-bvh
# 또는
yarn add @react-three/bvh three-mesh-bvh
  • Bvh 적용하려는 컴포넌트를 Bvh 컴포넌트로 감싸주기
<Canvas>
	<Bvh firstHitOnly>
		<Scene />
	</Bvh>
</Canvas>

firstHitOnly

  • ture가 default value
  • Ray의 가장 먼저 만나는 객체 하나만 찾고 Raycasting 종료
  • 가장 효율적

특징

  • 선언적으로 간편함
  • 자동화 관리
    내부적으로 자식 mesh 찾아 BVH 빌드, 컴포넌트 언마운트 시, BVH 메모리 해제 자동화

three-mesh-bvh

https://github.com/gkjohnson/three-mesh-bvh

  • 설치
npm install three-mesh-bvh
# 또는
yarn add three-mesh-bvh
  • BVH 관련 함수 import
import {
  acceleratedRaycast, // BVH 가속 Raycast 함수
  computeBoundsTree,  // BVH 트리를 계산하는 함수
  disposeBoundsTree   // BVH 트리를 해제하는 함수
} from 'three-mesh-bvh';
  • mesh geometry에 BVH 적용
useEffect(() => {
    // 모델의 모든 하위 메시를 순회하며 BVH 적용
    gltf.scene.traverse((child) => {
      if (child.isMesh) {
        // 3. 메시의 지오메트리에 BVH 트리 계산
        child.geometry.computeBoundsTree = computeBoundsTree; // BVH 빌드 함수 할당
        child.geometry.disposeBoundsTree = disposeBoundsTree; // BVH 해제 함수 할당 (클린업 시 사용)
        child.geometry.computeBoundsTree(); // 실제로 BVH 트리 빌드

        // (선택 사항) 만약 특정 메시에서 원래 Raycast 기능을 사용하고 싶다면,
        // child.raycast = originalRaycastFunction; 처럼 오버라이드 가능
      }
    });

    // 컴포넌트 언마운트 시 BVH 트리 메모리 해제 (선택 사항이지만 권장)
    return () => {
      gltf.scene.traverse((child) => {
        if (child.isMesh && child.geometry.boundsTree) {
          child.geometry.disposeBoundsTree();
        }
      });
    };
  }, [gltf.scene]); // gltf.scene이 변경될 때만 다시 실행
  • THREE.Mesh.prototype.raycast 오버라이드
THREE.Mesh.prototype.raycast = acceleratedRaycast;

특징

  • Ray 연산이 크게 줄어들어서 성능적으로 이득

  • 복잡한 model의 경우 BVH 빌드 시간이 필요

  • object의 위치, 회전, 크기등이 실시간으로 변하는 경우, 변하면 BVH 트리를 다시 빌드해야하므로, 이 경우에는 오히려 성능 저하

  • 특정 mesh에만 BVH를 적용하고 싶을 경우에 three-mesh-bvh가 유리

  • custom Raycaster 로직을 사용하는 경우 three-mesh-bvh가 유리

결론

  • 고정되어 있는 여러개의 object와의 interaction을 사용하는 경우 BVH가 유리하다.
profile
Web FrontEnd Developer

0개의 댓글