
네이버 랩스에서 3D 웹 개발을 하면서 여러 objects를 선택하고 수정하는 기능을 개발을 진행하였습니다. 3D 환경에서 objects를 단순히 시각화 뿐만 아니라, objects와 상호작용은 필수적입니다. 그러면서 Raycasting을 정말 많이 사용하게되었습니다. 이 글은 Raycasting을 정확히 이해하고 사용하기 위해서 작성하였습니다.
참고
https://threejs.org/docs/#api/en/core/Raycaster
https://github.com/gkjohnson/three-mesh-bvh
https://codesandbox.io/p/sandbox/r3f-mesh-bvh-sri15
https://threejs.org/examples/webgl_raycaster_bvh.html
Ray를 특정 방향으로 Casting
특정 시작점(Origin)에서 특정 방향(Direction)으로 Ray를 생성하여
3D Scene에 배치된 객체와 Intersect 여부를 감지하는 기술
Raycasting을 여러 경우에 사용이 가능하다.
이번 예제에서는 Mouse 위치에서 Raycasting이 되는 것을 소개
import * as THREE from 'three';
// Raycaster 인스턴스 생성
const raycaster = new THREE.Raycaster();

// 마우스 좌표를 저장할 Vector2 인스턴스 생성
const mouse = new THREE.Vector2();
function onMouseMove(event) {
// 마우스의 클라이언트 좌표 (픽셀 단위)를 가져옵니다.
// event.clientX: 뷰포트 기준 X 좌표
// event.clientY: 뷰포트 기준 Y 좌표
// 클라이언트 좌표를 NDC(-1.0 ~ +1.0)로 변환합니다.
// X축: (마우스 X / 뷰포트 너비) * 2 - 1
// Y축: -(마우스 Y / 뷰포트 높이) * 2 + 1 <-- Y축은 Three.js와 웹 표준이 반대이므로 -를 붙여줍니다.
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// 'mousemove' 이벤트를 리스닝하여 마우스 위치를 추적합니다.
window.addEventListener('mousemove', onMouseMove, false);
Y축에 -인 이유
웹 브라우저 화면 좌표계는 왼쪽 상단이 0,0이고 오른쪽 아래가 1,1이다.
즉, Y값이 아래로 갈수록 증가한다.
Three.js에서는 왼쪽 하단이 -1,-1이고 오른쪽 상단이 1,1이다.
따라서 Y축 좌표의 경우 웹 브루오저와 Three.j의 방향 차이가 있으므로 -1을 곱해준다.
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 1. Raycaster 업데이트
// mouse (NDC 좌표)와 camera를 사용하여 광선의 origin과 direction을 설정합니다.
raycaster.setFromCamera(mouse, camera);
// 2. 3D 객체들과의 교차 감지
// intersectObjects(objects, recursive):
// - objects: 교차를 감지할 THREE.Object3D 객체들의 배열입니다.
// scene.children을 직접 사용하는 것보다, 인터랙션이 필요한 특정 객체들만 모아둔 배열을 전달하는 것이 성능에 좋습니다.
// - recursive: (선택 사항, 기본값 false) true로 설정하면 objects 배열 내의 각 객체들의 자식 객체들까지 재귀적으로 검사합니다.
const intersects = raycaster.intersectObjects(scene.children, true); // scene의 모든 자식 객체를 재귀적으로 검사
}
IntersectObjects()
IntersectObjects() 메서드는 교차되는 모든 객체에 대한 정보를 담은 배열을 반환한다.
거리가 가까운 순서대로 정렬된다.
intersection 객체
intersection 객체는 여러 유용한 정보를 포함한다.
- distance
- point
- face
- faceIndex
- object
- uv
- instanceId


const origin = new THREE.Vector3(0, 0, 0); // (0,0,0)에서 시작
const direction = new THREE.Vector3(0, 1, 0).normalize(); // Y축 방향으로
raycaster.set(origin, direction);
// 마우스 이벤트로부터 클라이언트 좌표(event.clientX, event.clientY)를 얻은 후,
// 이를 NDC로 변환합니다.
const mouse = new THREE.Vector2();
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // Y축은 화면 하단이 -1, 상단이 +1
}
// 애니메이션 루프 또는 마우스 이벤트 핸들러 내부에서
raycaster.setFromCamera(mouse, camera);
PerspectiveCamera와 OrthographicCamera의 ray 계산 방식
- PerspectiveCamera: origin은 카메라 위치,
- OrthographicCamera: origin은 coords에 해당하는 카메라 시야 평면 상의 3D 지점이 되며, direction은 카메라가 바라보는 방향과 동일합니다.
intersection 검사 이후, intersection되는 객체에 대한 정보를 배열에 담아서 제공해준다.
반환되는 객체의 속성
https://threejs.org/docs/#api/en/core/Raycaster.intersectObject
distance
distanceToRay
point
face
faceIndex
object
uv
uv1
normal
instanceId