Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
示例代码采用three.js-r73版本: github.com/mrdoob/thre…
我们现在做了场景渲染物体,通过监听鼠标来改变场景视角,那我们如何通过鼠标来选中物体呢?比如选中物体变色。让我们来看看吧。
我们使用上节渲染的随机几何体来完成鼠标选中功能。
什么是拾取
- 鼠标选中一个物体就是拾取。
我们在二维视图中来描述一下三维鼠标点击情况
- 假设我们右侧相机看向场景中的物体
- 当我们鼠标点击某个位置之后
- 鼠标和相机之间的连线穿过的物体(和物体相交),就是我们可以拾取到的
- 我们可以判断只有第一个相交的物体,然后对其做处理
光线投射Raycaster
- 这个类用于进行raycasting(光线投射)。 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
- Raycaster类是用来实现拾取的一个简单类。说他简单,是因为,你基本不需要去了解他的原理,就可以使用他。
- 构造函数
THREE.Raycaster = function ( origin, direction, near, far )
复制代码
- origin:光纤发射出去的地方
- direction:归一化的方向向量
- near:光纤发射最近的距离,默认为0
- far:光纤发射最远的距离,默认为无穷远
注意:如果不传参数,那么这条光线原点和方向都为默认的原点。
setFromCamera方法
setFromCamera: function ( coords, camera )
复制代码
- 这个方法可以通过相机来设置
origin, direction
两个属性 - coords: 鼠标的位置,是一个归一化的设备坐标,必须在-1到1之间
- camera:光线起源的位置
归一化坐标
- 归一化坐标优点:我们摄像机使用的是三维世界的世界坐标,通过归一化坐标比较好转化。
- 假设我们三维世界中归一化坐标在原点,那我们屏幕坐标在左上角,所以需要通过转化函数,把屏幕坐标转化为归一化坐标
intersectObject方法
intersectObject: function ( object, recursive )
复制代码
- 这个方法可以计算哪些对象和他相交
- object —— 检查与射线相交的物体。
- recursive —— 若为true,则同时也会检查所有的后代。否则将只会检查对象本身。默认值为false。
实战
- 初始化raycaster
raycaster = new THREE.Raycaster();
复制代码
- 监听鼠标移动,记录坐标信息
// 鼠标的默认位置是一个二维向量
var mouse = new THREE.Vector2()
document.addEventListener("mousemove", onDocumentMouseMove, false);
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
复制代码
- 计算出相机照射到鼠标的光线
raycaster.setFromCamera(mouse, camera);
复制代码
- 找到 和射线相交的对象,返回数组(相交的物体不只有一个)
var intersects = raycaster.intersectObjects(scene.children);
复制代码
- 把当前选中的物体设置为红色,之前选中和未选中的设置为原来的颜色(这里逻辑比较复杂我们用一张图来解释)
// 如果有相交的物体
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
// 这里选中的物体是上一个选中物体。
if (INTERSECTED) {
// 把上一个选中的物体设置为当前色。
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
}
// 设置当前选中的物体
INTERSECTED = intersects[0].object;
// 保留当前选中物体,**原本的颜色**
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
// 设置当前选中的物体颜色为红色
INTERSECTED.material.emissive.setHex(0xff0000);
}
// 如果没有相交的物体,把选中的物体设置为原来的颜色
} else {
if (INTERSECTED) {
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
}
// 清空选中物体
INTERSECTED = null;
}
复制代码
实现效果
- 鼠标选中物体会变色,离开之后选中的物体变回原来的颜色
- 下一个选中物体变色
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END