raycast mode 동작 : 마우스로 raycast 쏘기
마우스 이벤트를 사용하는데 있어서 JS기반으로 동작하기 때문에 event처리가 바로 생각나지 않는다
한 번 테스트 코드를 짜볼까
raycast로 canvas상 2d 위치를 찍는건 금방하지만,
3D 환경에서 object intersect를 구현하려면 카메라가 필요하다
import { Camera, Raycaster, Scene, Vector2 } from "three"
let raycaster = new Raycaster()
const mouse = new Vector2()
export function init(scene: Scene, camera: Camera): Raycaster{
window.addEventListener('mousemove', onMouseMove, false)
window.addEventListener('click', ()=>clickPoint(scene, camera), false)
return raycaster
}
function onMouseMove(event: MouseEvent) {
// Normalize mouse coordinates to -1 to 1 range
mouse.x = (event.clientX / window.innerWidth) * 2 - 1
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1
}
export function clickPoint(scene: Scene, camera: Camera){
raycaster.setFromCamera(mouse, camera)
const intersects = raycaster.intersectObjects(scene.children)
console.log(intersects.length)
if (intersects.length > 0) {
const intersect = intersects[0]
const intersectedObject = intersect.object
const intersectPoint = intersect.point
console.log('Intersected object:', intersectedObject)
console.log('Intersection point:', intersectPoint)
}
}
이렇게 카메라에서 ray를 쏘고 탐지하는 식으로 구현하였는데
카메라를 움직이면 raycasting이 좀 이상해진다
camera controler를 사용해서 그런 것 같기도 하다
raycast drawLine 선 그리기
function drawLine(scene: Scene){
let material = new LineBasicMaterial({
color: 0xff0000,
linewidth: 10
})
let startVec = new Vector3(
raycaster.ray.origin.x,
raycaster.ray.origin.y,
raycaster.ray.origin.z)
let endVec = new Vector3(
raycaster.ray.direction.x,
raycaster.ray.direction.y,
raycaster.ray.direction.z)
// could be any number
endVec.multiplyScalar(5000)
// get the point in the middle
let midVec = new Vector3()
midVec.lerpVectors(startVec, endVec, 0.5)
let geometry = new BufferGeometry().setFromPoints([startVec, endVec])
console.log('vec start', startVec)
console.log('vec mid', midVec)
console.log('vec end', endVec)
let line = new Line(geometry, material)
scene.add(line)
}
함수를 추가해서 ray를 쏜 경로를 시각화 해보자
카메라로부터 클릭한 포인트로 선이 그려진다
드래그를 하려고 해도 ray line이 찍히는게 좀 거슬리긴 하는데
문제는 계속 쌓이고 끝이 없다
그리고 무엇보다 클릭한 지점을 보고 디버깅하려는 목적인데,
카메라에서 정확히 정면으로 쏘다보니 ray를 쏜 시점에서는 line이 안보인다
즉 camera가 아니라 다른 point를 origin으로 ray를 쏴야 보일 것이다
import { BufferGeometry, Camera, Line, LineBasicMaterial, Ray, Raycaster, Scene, Vector2, Vector3 } from "three"
let raycaster = new Raycaster()
const mouse = new Vector2()
let gizmoLine: Line = new Line()
export function init(scene: Scene, camera: Camera): Raycaster{
window.addEventListener('mousemove', onMouseMove, false)
window.addEventListener('click', ()=>clickPoint(scene, camera), false)
return raycaster
}
function onMouseMove(event: MouseEvent) {
// Normalize mouse coordinates to -1 to 1 range
mouse.x = (event.clientX / window.innerWidth) * 2 - 1
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1
}
export function clickPoint(scene: Scene, camera: Camera){
raycaster.setFromCamera(mouse, camera)
// console.log(camera.position)
// console.log(camera.rotation)
const intersects = raycaster.intersectObjects(scene.children)
// console.log(intersects.length)
drawLine(scene)
if (intersects.length > 0) {
const intersect = intersects[0]
const intersectedObject = intersect.object
const intersectPoint = intersect.point
// console.log('Intersected object:', intersectedObject)
// console.log('Intersection point:', intersectPoint)
}
}
function drawLine(scene: Scene){
let material = new LineBasicMaterial({
color: 0xff0000,
linewidth: 10
})
let startVec = new Vector3(
raycaster.ray.origin.x,
raycaster.ray.origin.y,
raycaster.ray.origin.z)
let endVec = new Vector3(
raycaster.ray.direction.x,
raycaster.ray.direction.y,
raycaster.ray.direction.z)
// could be any number
endVec.multiplyScalar(5000)
// get the point in the middle
let midVec = new Vector3()
midVec.lerpVectors(startVec, endVec, 0.5)
let geometry = new BufferGeometry().setFromPoints([startVec, endVec])
console.log('vec start', startVec)
console.log('vec mid', midVec)
console.log('vec end', endVec)
gizmoLine.geometry = geometry
gizmoLine.material = material
scene.add(gizmoLine)
}
이렇게 하여 하나의 line만 그리도록 했고,
line을 그릴 때 origin을 camera보다 조금 위 혹은 조금 아래 위치에서 쏘도록 해보자.
그리고 보면 현재 object에 맞춰서 쏘는게 아니라 카메라 방향만 따라서 저~ 끝을 쏘고 있다
목적지를 클릭한 interesection point로 바꾸자
갑자기 길이가 짧아지길래 당황했는데
이게.. line도 오브젝트라고 이미 쏜 line에 부딪힌 것도 intersect라고 간주하는 모양이다
실제로 감지한 오브젝트가 Line이다
drawLine이 스스로 line을 감지하지 않기
if (intersects.length > 0 && intersects[0].object !== gizmoLine) {
const intersect = intersects[0]
const intersectedObject = intersect.object
const intersectPoint = intersect.point
drawLine(scene, camera.position, intersectPoint)
// console.log('Intersected object:', intersectedObject)
// console.log('Intersection point:', intersectPoint)
}
이렇게 이미 그린 gizmoLine과 intersect object가 다른지를 확인하는 조건 추가
판 감지
저 빨간색만 있는 부분은 감지가 잘 안되는데 왜그럴까
custom mesh 에서 threejs raycast interect 안됨
plan geometry의 경우 잘 감지 하지만 custom obj 파일로 만든 mesh는 감지가 잘 안되는 느낌이다
https://stackoverflow.com/questions/58800187/three-js-raycasting-on-custom-mesh
실제로 나와 같이 느낀 사람이 있었다
이 사람은 compute bounding sphere 라는 함수를 통해 해결했다고 한다
이게 뭐지?