three.js(21)-元素拾取
2022-12-30 本文已影响0人
姜治宇
光线投射
以智慧园区为例。
当我们点击某一个建筑物模型时,页面就会弹出一个对话框,告知这个建筑的有关信息,这是如何做到的呢?
我们可以用raycaster光线投射来解决。
光线投射,就是向特性方向发出射线,并测试哪些对象与之相交。
GIF 2022-12-31 11-47-47.gif
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://www.yanhuangxueyuan.com/threejs/build/three.min.js"></script>
<script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<div id="webgl"></div>
</body>
</html>
<script>
//场景
var scene = new THREE.Scene();
//相机设置为世界坐标原点
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 10);
scene.add(camera);//添加相机
//添加坐标轴
var axes = new THREE.AxesHelper(500);//500表示xyz轴的长度,红:x,绿:y,蓝:z
scene.add(axes);
//设置物体
const object1 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5,10,10),
new THREE.MeshBasicMaterial({color:'#ff0000'})
);
const object2 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5,10,10),
new THREE.MeshBasicMaterial({color:'#ff0000'})
);
const object3 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5,10,10),
new THREE.MeshBasicMaterial({color:'#ff0000'})
);
object1.position.x = 2;
object2.position.x = 5;
object3.position.x = 7;
scene.add(object1,object2,object3);//添加物体
var renderer = new THREE.WebGLRenderer();//画布
renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
//将渲染好的canvas追加到dom
var cont = document.getElementById('webgl');
cont.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls( camera, renderer.domElement );
const raycaster = new THREE.Raycaster();
function raycast(raycaster){
//设置射线
const rayOrigin = new THREE.Vector3(0,0,0);//从原点出发射一根射线
const rayDir = new THREE.Vector3(1,0,0);//方向向量
raycaster.set(rayOrigin,rayDir);
//未射中的还是红色
const objs = [object1,object2,object3];
for(const obj of objs){
obj.material.color.set('#ff0000');
}
//射中的为绿色
const intersects = raycaster.intersectObjects([object1,object2,object3]);
for(const intersect of intersects) {
intersect.object.material.color.set('#00ff00');
}
}
const clock = new THREE.Clock();
function animate(){
const elaTime = clock.getElapsedTime();
//不断改变物体的位置
object1.position.y = Math.sin(elaTime*0.5)*1.5;
object2.position.y = Math.sin(elaTime*0.8)*1.5;
object3.position.y = Math.sin(elaTime*1.2)*1.5;
raycast(raycaster);
controls.update();
renderer.render(scene, camera);//开始渲染
requestAnimationFrame(animate);
}
animate();
</script>
通过鼠标发射射线
鼠标只在屏幕移动,所以使用二维向量来创建鼠标变量即可。
设想一下,如果从相机到鼠标的位置连一根线(向量减法),这样就可以得到一条方向向量。沿着这个方向向量发射一条射线,击中的就是我们点击的目标物体。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://www.yanhuangxueyuan.com/threejs/build/three.min.js"></script>
<script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<div id="webgl"></div>
</body>
</html>
<script>
//场景
var scene = new THREE.Scene();
//相机设置为世界坐标原点
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 10);
scene.add(camera);//添加相机
//添加坐标轴
var axes = new THREE.AxesHelper(500);//500表示xyz轴的长度,红:x,绿:y,蓝:z
scene.add(axes);
//设置物体
const object1 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5, 10, 10),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
);
const object2 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5, 10, 10),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
);
const object3 = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.5, 10, 10),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
);
object1.name = 'object1';
object2.name = 'object2';
object3.name = 'object3';
object1.position.x = 2;
object2.position.x = 5;
object3.position.x = 7;
scene.add(object1, object2, object3);//添加物体
const size = {
width: window.innerWidth,
height: window.innerHeight
};
var renderer = new THREE.WebGLRenderer();//画布
renderer.setSize(size.width, size.height);//设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
//将渲染好的canvas追加到dom
var cont = document.getElementById('webgl');
cont.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
const mouse = new THREE.Vector2(0, 0);
window.addEventListener('mousemove', e => {
mouse.x = (e.clientX / size.width * 2) - 1;
mouse.y = -(e.clientY / size.height * 2) + 1;
});
window.addEventListener('click', e => {
if (currentIntersect) {
console.log('点击了物体》》》', currentIntersect.object.name);
}
});
const raycaster = new THREE.Raycaster();
let currentIntersect = null;//当前被射线击中的物体
function raycast(raycaster) {
//设置射线
raycaster.setFromCamera(mouse, camera);
// const rayOrigin = new THREE.Vector3(0,0,0);//从原点出发射一根射线
// const rayDir = new THREE.Vector3(1,0,0);//方向向量
// raycaster.set(rayOrigin,rayDir);
//未射中的还是红色
// const objs = [object1,object2,object3];
// for(const obj of objs){
// obj.material.color.set('#ff0000');
// }
const intersects = raycaster.intersectObjects([object1, object2, object3]);
//射中的为绿色
// for(const intersect of intersects) {
// intersect.object.material.color.set('#00ff00');
// }
if (intersects.length > 0) {
currentIntersect = intersects[0];
} else {
currentIntersect = null;
}
}
const clock = new THREE.Clock();
function animate() {
const elaTime = clock.getElapsedTime();
//不断改变物体的位置
object1.position.y = Math.sin(elaTime * 0.5) * 1.5;
object2.position.y = Math.sin(elaTime * 0.8) * 1.5;
object3.position.y = Math.sin(elaTime * 1.2) * 1.5;
raycast(raycaster);
controls.update();
renderer.render(scene, camera);//开始渲染
requestAnimationFrame(animate);
}
animate();
</script>
GIF 2022-12-31 12-19-45.gif