【threejs】光线投射点击物体

光线投射

光线投射用于进行鼠标拾取, 在三维空间中计算出鼠标移过了什么物体

文档: https://threejs.rocyuan.top/docs/?q=ray#api/zh/core/Raycaster

示例代码

完成一个示例, 三个立方体都为绿色, 点击哪个立方体, 就将其变为红色, 再次点击, 同样的逻辑, 将其他立方体变为绿色

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
<template>
<div ref="threeContainer" class="three-container"></div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

const threeContainer = ref(null);

function init3D() {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, threeContainer.value.clientWidth / threeContainer.value.clientHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight);
threeContainer.value.appendChild(renderer.domElement);

new OrbitControls(camera, renderer.domElement);

function createCube(width = 1, height = 1, depth = 1) {
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
return new THREE.Mesh(geometry, material);
}
const cube1 = createCube();
cube1.position.x = -2;
const cube2 = createCube();
const cube3 = createCube();
cube3.position.x = 2;
scene.add(cube1, cube2, cube3);

function cubeAllGreen() {
cube1.material.color.set(0x00ff00);
cube2.material.color.set(0x00ff00);
cube3.material.color.set(0x00ff00);
}

// 1. 创建光线投射
const raycaster = new THREE.Raycaster();
// 2. 创建二维向量存储鼠标位置
const mouse = new THREE.Vector2();

threeContainer.value.addEventListener("mousedown", (event) => {
// 3. 将鼠标点击 canvas 的像素坐标转换为三维场景中的 x y 二维坐标 取值范围是 -1 到 1
mouse.x = (event.clientX / threeContainer.value.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / threeContainer.value.clientHeight) * 2 + 1;
// 4. 通过鼠标位置和摄像机更新射线
raycaster.setFromCamera(mouse, camera);
// 5. 通过射线和场景中的所有物体进行相交检测
const intersects = raycaster.intersectObjects(scene.children);
// 6. 如果有交点,输出第一个交点的对象(从相机发射出一条射线,会穿过多个物体,所以会返回多个交点,第一个交点就是鼠标点击距离相机最近的物体)
cubeAllGreen();
if (intersects.length > 0) {
const currentClickMesh = intersects[0].object;
currentClickMesh.material.color.set(0xff0000);
}
});

function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}

onMounted(() => {
init3D();
});
</script>

<style scoped>
.three-container {
width: 100vw;
height: calc(100vh - 20px);
}
</style>

效果

射线点击物体