Skip to content

Raycaster

Video Lecture

Raycaster Raycaster

Description

Raycasting allows you to create a vector from a 3D point in the scene, and detect which object(s) the vector intersects.

The raycasting class is mostly used for mouse picking objects in the 3D scene.

We can set up the Raycaster position and direction using the set or setFromCamera methods and then call its intersectObject or intersectObjects methods to tell us many things about the scene objects that were intersected by the ray, including,

  • the distance of the intersection from the Raycaster position,
  • the position of the intersection in the 3D scene,
  • the face of the object that was intersected,
  • the direction of the faces normal,
  • the UV coordinate of the intersection on the face
  • and a reference to the intersected object itself.
<>

Lesson Script

The 3D model used in this lesson was created in the GLTFLoader Part 2 lesson.

./src/main.ts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import Stats from 'three/addons/libs/stats.module.js'

const scene = new THREE.Scene()

new RGBELoader().load('img/venice_sunset_1k.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping
  scene.environment = texture
  scene.background = texture
  scene.backgroundBlurriness = 0.5
})

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100)
camera.position.set(0, 0, 3)

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.8
renderer.shadowMap.enabled = true
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

// const raycaster = new THREE.Raycaster()
// const pickables: THREE.Mesh[] = []
// const mouse = new THREE.Vector2()

// const arrowHelper = new THREE.ArrowHelper()
// arrowHelper.setLength(0.5)
// scene.add(arrowHelper)

// renderer.domElement.addEventListener('mousemove', (e) => {
//   mouse.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1)

//   raycaster.setFromCamera(mouse, camera)

//   const intersects = raycaster.intersectObjects(pickables, false)

//   if (intersects.length) {
//     //console.log(intersects.length)
//     //console.log(intersects[0].point)
//     //console.log(intersects[0].object.name + ' ' + intersects[0].distance)
//     //console.log((intersects[0].face as THREE.Face).normal)

//     const n = new THREE.Vector3()
//     n.copy((intersects[0].face as THREE.Face).normal)
//     //n.transformDirection(intersects[0].object.matrixWorld)

//     arrowHelper.setDirection(n)
//     arrowHelper.position.copy(intersects[0].point)
//   }
// })

// renderer.domElement.addEventListener('dblclick', (e) => {
//   mouse.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1)

//   raycaster.setFromCamera(mouse, camera)

//   const intersects = raycaster.intersectObjects(pickables, false)

//   if (intersects.length) {
//     const n = new THREE.Vector3()
//     n.copy((intersects[0].face as THREE.Face).normal)
//     //n.transformDirection(intersects[0].object.matrixWorld)

//     const cube = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.2, 0.2), new THREE.MeshStandardMaterial())
//     cube.lookAt(n)
//     cube.position.copy(intersects[0].point)
//     cube.position.addScaledVector(n, 0.1)
//     cube.castShadow = true

//     scene.add(cube)
//     pickables.push(cube)
//   }
// })

new GLTFLoader().load('models/suzanne_scene.glb', (gltf) => {
  const suzanne = gltf.scene.getObjectByName('Suzanne') as THREE.Mesh
  suzanne.castShadow = true
  // @ts-ignore
  suzanne.material.map.colorSpace = THREE.LinearSRGBColorSpace
  //pickables.push(suzanne)

  const plane = gltf.scene.getObjectByName('Plane') as THREE.Mesh
  plane.receiveShadow = true
  //pickables.push(plane)

  const spotLight = gltf.scene.getObjectByName('Spot') as THREE.SpotLight
  spotLight.intensity /= 500
  spotLight.castShadow = true

  scene.add(gltf.scene)
})

const stats = new Stats()
document.body.appendChild(stats.dom)

function animate() {
  requestAnimationFrame(animate)

  controls.update()

  renderer.render(scene, camera)

  stats.update()
}

animate()

Raycaster (threejs.org)

Comments