Threejs之射线拾取模型
参考资料
- 射线Ray
- …
- 射线拾取Sprite控制场景
知识点
注:基于Three.jsv0.155.0
- 射线Ray
- Raycaster(射线拾取模型)
- 屏幕坐标转标准设备坐标
- Raycaster(鼠标点击选中模型)
- Canvas尺寸变化(射线坐标计算)
- 射线拾取层级模型(模型描边)
- 射线拾取Sprite控制场景
代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Three.js</title>
</head>
<body>
</body>
<!-- 具体路径配置,你根据自己文件目录设置,我的是课件中源码形式 -->
<script type="importmap">
{
"imports": {
"three": "./js/three.module.js",
"three/addons/": "../three.js/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const width = 800
const height = 500
// 场景
const scene = new THREE.Scene();
// 几何体
const geometry = new THREE.BoxGeometry(50, 50, 50);
// 材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
transparent: true,
opacity: 0.5
});
// 网格模型:物体
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
scene.add(mesh);
const mesh1 = mesh.clone()
mesh1.position.set(100, 0, 0);
mesh1.material = mesh.material.clone();
scene.add(mesh1);
const mesh2 = mesh.clone()
mesh2.position.set(0, 100, 0);
mesh2.material = mesh.material.clone();
scene.add(mesh2);
// 坐标系
const axes = new THREE.AxesHelper(200);
scene.add(axes);
// 相机
const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000);
camera.position.set(200, 200, 200);
camera.lookAt(scene.position);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.render(scene, camera);
// 设置设备像素比,避免canvas画布输出模糊
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// 射线Ray Start
// 创建射线对象Ray
const ray = new THREE.Ray()
// 设置射线起点
ray.origin = new THREE.Vector3(1,0,3);
// 表示射线沿着x轴正方向
ray.direction = new THREE.Vector3(1,0,0);
// 三角形三个点坐标
const p1 = new THREE.Vector3(100, 25, 0);
const p2 = new THREE.Vector3(100, -25, 25);
const p3 = new THREE.Vector3(100, -25, -25);
const point = new THREE.Vector3();//用来记录射线和三角形的交叉点
// `.intersectTriangle()`计算射线和三角形是否相交叉,相交返回交点,不相交返回null
const result = ray.intersectTriangle(p1,p2,p3,true,point);
console.log('交叉点坐标', point);
console.log('查看是否相交', result);
// 射线Ray End
// 3. 屏幕坐标转标准设备坐标 Start
addEventListener('click',function(event){
// event对象有很多鼠标事件相关信息
console.log('event',event);
const px = event.offsetX;
const py = event.offsetY;
//屏幕坐标px、py转标准设备坐标x、y
//width、height表示canvas画布宽高度
const x = (px / width) * 2 - 1;
const y = -(py / height) * 2 + 1;
console.log('🚀 ~ file: 14.射线拾取模型.html:96 ~ addEventListener ~ y:', y)
//创建一个射线投射器`Raycaster`
const raycaster = new THREE.Raycaster();
//.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
// 形象点说就是在点击位置创建一条射线,射线穿过的模型代表选中
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
//.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
// 未选中对象返回空数组[],选中一个对象,数组1个元素,选中两个对象,数组两个元素
const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh]);
console.log("射线器返回的对象", intersects);
// intersects.length大于0说明,说明选中了模型
if (intersects.length > 0) {
// 选中模型的第一个模型,设置为红色
intersects[0].object.material.color.set(0x0000ff);
}
})
// 3. 屏幕坐标转标准设备坐标 Ene
// Raycaster(射线拾取模型) Start
const raycaster = new THREE.Raycaster();
console.log('射线属性',raycaster.ray);
// 设置射线起点
raycaster.ray.origin = new THREE.Vector3(-100, 0, 0);
// 设置射线方向射线方向沿着x轴
raycaster.ray.direction = new THREE.Vector3(1, 0, 0);
// 射线发射拾取模型对象
const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh]);
console.log("射线器返回的对象", intersects);
// intersects.length大于0说明,说明选中了模型
if (intersects.length > 0) {
console.log("交叉点坐标", intersects[0].point);
console.log("交叉对象",intersects[0].object);
console.log("射线原点和交叉点距离",intersects[0].distance);
intersects[0].object.material.color.set(0xff0000);
}
// Raycaster(射线拾取模型) End
// 动画渲染
// 跟踪时间
var clock = new THREE.Clock();
function render() {
const spt = clock.getDelta() * 1000;
// mesh.rotation.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', () => {
// 因为动画渲染了,所以这里可以省略
// renderer.render(scene, camera);
});
</script>
</html>