Raycaster
Video Lecture
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()
|
Useful Links
Raycaster (threejs.org)