射线追踪法
- 射线追踪法是最常见的方法,因为
three.js
提供了Raycaster
对象来实现它。 - 原理:从鼠标处发射一条射线,穿透场景的视椎体,通过计算,找出与射线相交的对象。
origin
光线投射的原点。direction
射线的方向。near
投射近点,不能大于far
,不能为负值,其默认值为0。far
投射远点,不能小于near
,其默认值为无穷大。
.setFromCamera(coords,camera)
更新原点坐标和相机视椎。coords
原点坐标,camera
相机。.intersectObject(scenes,recursive,optionalTarget)
检查射线与场景对象和其子集是否有交集。scenes
要检查的场景对象数组格式。recursive
为true
检查所有后代。optionalTarget
返回交集结果,可选。
使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>学习</title>
</head>
<body>
<canvas id="c2d" class="c2d" width="1000" height="500"></canvas>
<script type="module">
import * as THREE from './file/three.js-dev/build/three.module.js'
import { OrbitControls } from './file/three.js-dev/examples/jsm/controls/OrbitControls.js'
const canvas = document.querySelector('#c2d')
const renderer = new THREE.WebGLRenderer({ canvas })
const fov = 40
const aspect = 2
const near = 0.1
const far = 10000
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0, 6, 5)
camera.lookAt(0, 0, 0)
const controls = new OrbitControls(camera, canvas)
controls.update()
const scene = new THREE.Scene()
{
const color = 0xffffff
const intensity = 1
const light = new THREE.DirectionalLight(color, intensity)
light.position.set(-1, 10, 4)
scene.add(light)
}
{
const boxWidth = 1
const boxHeight = 1
const boxDepth = 1
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth)
const material = new THREE.MeshPhongMaterial({
color: 0x6688aa
})
const cube = new THREE.Mesh(geometry, material)
cube.position.x = -1
scene.add(cube)
const material2 = new THREE.MeshPhongMaterial({
color: 0x6688aa
})
const cube2 = new THREE.Mesh(geometry, material2)
cube2.position.x = 1
scene.add(cube2)
}
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
</script>
</body>
</html>
![image.png](https://img-blog.csdnimg.cn/img_convert/e57d1be98e397e754acef428c6f6c110.png)
使用.Raycaster()
- 第一步获取鼠标的屏幕坐标,将鼠标坐标转化成设备坐标(归一化),后续的操作都交由
.Raycaster()
完成。 - 这里非全屏使用画布,鼠标坐标以画布开始计算。
function getCanvasRelativePosition(event) {
const rect = canvas.getBoundingClientRect()
return {
x: ((event.clientX - rect.left) * canvas.width) / rect.width,
y: ((event.clientY - rect.top) * canvas.height) / rect.height
}
}
function setPickPosition(event) {
let pickPosition = { x: 0, y: 0 }
const pos = getCanvasRelativePosition(event)
pickPosition.x = (pos.x / canvas.width) * 2 - 1
pickPosition.y = (pos.y / canvas.height) * -2 + 1
return pickPosition
}
- 监听鼠标事件,获取归一化坐标。使用
.intersectObjects()
发出射线计算相交对象。 - 需要一个全局对象,用于鼠标离开后恢复对象的原样。
window.addEventListener('mousemove', onRay)
let lastPick = null
function onRay(event) {
let pickPosition = setPickPosition(event)
const raycaster = new THREE.Raycaster()
raycaster.setFromCamera(pickPosition, camera)
const intersects = raycaster.intersectObjects(scene.children, true)
if (intersects.length > 0) {
if (lastPick) {
lastPick.object.material.color.set('yellow')
}
lastPick = intersects[0]
} else {
if (lastPick) {
lastPick.object.material.color.set(0x6688aa)
lastPick = null
}
}
}
![1.gif](https://img-blog.csdnimg.cn/img_convert/4d7d3b541d5648486673bdd986433c8b.gif)
- 就这样一个简单鼠标拾取就完成了。
- 除了射线追踪法还可以使用GPU拾取,它是利用颜色的6位16进制表示,以颜色作为ID,在后台渲染出纹理后。然后,检查鼠标位置关联的像素的颜色,通过颜色来确认相交的对象是哪个。