Three.js开发必备:射线拾取模型
射线
射线Ray
类比数学几何提到的射线,三维控件中,一条线把一个点作为起点,沿着某个方向无限延伸。
const ray = new THREE.Ray()
射线起点.origin
射线Ray
的起点.origin
在3D
空间中的坐标,可以用一个三维向量Vector3
的x,y,z
分量表示。
ray.origin = new THREE.Vector3(0, 0, 0);
起点.origin属性值是三维向量Vector3
,也可以用.set()
方法设置。
ray.origin.set(0, 0, 0);
射线方向.direction
射线Ray
的方向.direction
通用用一个三维向量Vector3
表示,向量长度保证为1
,也就是单位向量。
// 表示射线沿着X轴的正方向
ray.direction = new THREE.Vector3(1, 0, 0);
// 表示射线沿着X轴的负方向
ray.direction = new THREE.Vector3(-1, 0, 0);
需要注意的是direction
的值需要时单位向量,不是的话可执行.normalize()
归一化或者说标准化。
ray.direction = new THREE.Vector3(5, 0, 0).normalize();
3D是否交叉.intersectTriangle()方法
射线Ray
有很多数学计算方法,.intersectTriangle()
就是一个计算一个射线和一个三角形在3D
空间中是否交叉。执行.intersectTriangle()
,如果有交叉返回交点坐标,如果没有则返回空值null
。
intersectTriangle()
参数4
表示是否进行背面剔除,p1,p2,p3
可以理解为一个三角形有正反两个面,一面是正面一面是反面,在一面观察p1,p2,p3
如果沿着三个点顺序转圈是逆时针方向表示正面,另一面观察p1,p2,p3
是顺时针方向表示背面。
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
//类型数组创建顶点数据
const vertices = new Float32Array([
100, 25, 0, //顶点1坐标
100, -25, 25, //顶点2坐标
100, -25, -25, //顶点3坐标
]);
// 创建属性缓冲区对象
const attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
// 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;
const material = new THREE.MeshBasicMaterial({
color: 0x00ffff, //材质颜色
side: THREE.FrontSide, //默认只有正面可见
// side: THREE.BackSide, //设置只有背面可见
// side: THREE.DoubleSide, //两面可见
});
// 网格模型本质:一个一个三角形(面)构成
const mesh = new THREE.Mesh(geometry, material);
const ray = new THREE.Ray()
ray.origin.set(0, 0, 0);
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(); // 用来记录射线和三角形的交叉点
const result = ray.intersectTriangle(p1, p2, p3, false, point)
console.log("交叉点坐标", point)
console.log("查看是否相交", result)
export default mesh;
效果图:
如果将ray.direction = new THREE.Vector3(1, 0, 0);
改为
ray.direction = new THREE.Vector3(-1, 0, 0);
如果为负值,则x
方向为负的射线没有交叉点,所以为null
。
如果从背面剔除,x
与剔除背面无交叉,也为null
。
ray.direction = new THREE.Vector3(1, 0, 0);
const result2 = ray.intersectTriangle(p1, p2, p3, true, point);
console.log('查看是否相交', result2);
Raycaster(射线拾取模型)
Raycaster
是一个和射线相关的射线投射器。
射线投射器Raycaster
射线投射器Raycaster
具有一个射线属性.ray
,该属性的值就是与Ray
相关的.direction
与.origin
。
const reycaster = new THREE.Raycaster();
console.log("射线属性", reycaster.ray);
const reycaster = new THREE.Raycaster();
reycaster.ray.origin.set(-100, 0, 0);
reycaster.ray.direction.set(1, 0, 0);
console.log("射线属性", reycaster.ray);
射线拾取返回信息.intersectObjects()
射线拾取返回的intersects
里面的元素包含多个信息,.intersectObjects()
与.intersectObject()
功能相同,只是具体语法不同,.intersectObjects()
返回数组元素包含的信息。
const raycaster = new THREE.Raycaster();
raycaster.ray.origin.set(-100, 0, 0);
raycaster.ray.direction = new THREE.Vector3(1, 0, 0);
const result = raycaster.intersectObjects([mesh1, mesh2, mesh3]);
console.log("射线属性", result);
交叉点坐标
const raycaster = new THREE.Raycaster();
raycaster.ray.origin.set(-100, 0, 0);
raycaster.ray.direction = new THREE.Vector3(1, 0, 0);
const result = raycaster.intersectObjects([mesh1, mesh2, mesh3]);
if (result.length > 0) {
console.log("交叉点坐标", result[0].point);
console.log("射线原点和交叉点距离", result[0].distance);
console.log("交叉对象", result[0].object);
}
设置交叉点物体的.material
设置射线拾取物体的属性颜色
result[0].object.material.color.set(0xff0000);
result[1].object.material.color.set(0xff0000);
Raycaster(鼠标点击选中模型)
开发中射线投射器Raycaster用到的很经常,鼠标点击选中一个模型对象,根据以下步骤进行设置即可。
第一步、坐标转化(屏幕坐标转标准设备桌标)
addEventListener('click', function (event) {
const px = event.offsetX;
const py = event.offsetY;
//屏幕坐标px、py转WebGL标准设备坐标x、y
//width、height表示canvas画布宽高度
const x = (px / width) * 2 - 1;
const y = -(py / height) * 2 + 1;
console.