개발 · 컴퓨터공학/three.js / / 2024. 6. 21. 09:34

Threejs Cloth Tailor 개발일지 - raycast line 그리기, 오브젝트 감지, intersectObjects, raycast intersect custom mesh

728x90
반응형

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

 

Three.JS - Raycasting on custom mesh

I'm currently trying to raycast a custom created mesh in three.js. While raycasting works like a charm for some imported meshes, it just doesn't seem to work at all with my custom mesh. After

stackoverflow.com

실제로 나와 같이 느낀 사람이 있었다 

이 사람은 compute bounding sphere 라는 함수를 통해 해결했다고 한다

이게 뭐지?

 

 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유