Threejs_10 光线投射技术完成画布三维事件交互

你完成了一个threejs的模型之后,里面有很多东西,你咋知道你点击的是哪个呢??如何触发你点击的事件呢?再canvas画布中可不能和html事件一样直接使用e.target来完成了哦。如何做到呢?

光线投射实现三维定位

方法思想

方法就是从点击的地方创造一条虚拟的从相机射入的射线,来计算这条射线是否穿过了什么几何体,穿过了几个,然后通过方法获取到穿过物体的属性。

1.创建三个小球,将其放入场景中

// 创建三个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  })
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x0000ff,
  })
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0xff00ff,
  })
);
sphere3.position.x = 4;
scene.add(sphere3);

 如果觉得三个球离得太近 可以把相机的z轴位置调高一点  也就是我们的眼睛靠后一点

camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);

2.新建射线,新建鼠标向量

// 创建射线
const raycaster = new THREE.Raycaster();

//创建鼠标向量
const mouse = new THREE.Vector2();

使用THREE.Raycaster 方法创建一个射线,鼠标向量用来存储后面得到的坐标值

3. window事件 获取点击到的地方

window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值
});

4.公式计算出对应的坐标值 传入鼠标向量中

//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值

  //设置鼠标向量的xy值
  //公式 可以用边框四个点做测试 或者暂时先记住
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);
});

 公式是为什么可以用四个点测试一下,然后自己思考一下。或者直接套用。得到的x,y的值就是平面像素转换为三维坐标的x,y值。

5.通过鼠标向量更新射线坐标

window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值

  //设置鼠标向量的xy值
  //公式 可以用边框四个点做测试 或者暂时先记住
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);

  //通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(mouse, camera);
});

6. 使用raycaster.intersectObjects方法计算物体和射线的焦点

window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值

  //设置鼠标向量的xy值
  //公式 可以用边框四个点做测试 或者暂时先记住
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);

  //通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(mouse, camera);

  //计算物体和射线的焦点
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
  console.log(intersects);
});

点击空的地方得到的就是一个空数组,点击有球的地方,得到的就是一个数组,数组值中有object属性,代表的就是这个小球的内容,可以通过object属性来设置小球的参数。

7.交互Demo实现

当然 也有可能出现两个的情况,所以说我们这时候只用取数组第一个值,用它来设置就好了。我们现在用他来做一个点击就变成红色的交互效果,要求是第二次点击的时候 要变回原有的颜色。

我们的思路就是需要再点击的时候判断他是否已经变过色了,如果没有变过色,就让他变色,并且将现在的颜色存起来。如果变过色,就让他的颜色变回来。

//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值

  //设置鼠标向量的xy值
  //公式 可以用边框四个点做测试 或者暂时先记住
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);

  //通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(mouse, camera);

  //计算物体和射线的焦点
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
  // console.log(intersects);
  if (intersects[0].object._isSelect) {
    //设定旧的颜色
    intersects[0].object.material.color.set(intersects[0].object._originColor);
    intersects[0].object._isSelect = false;
    return;
  }
  //添加一个属性 选中状态
  intersects[0].object._isSelect = true;
  //将之前的颜色记录下来
  intersects[0].object._originColor =
    intersects[0].object.material.color.getHex();
  //设定新的颜色
  intersects[0].object.material.color.set(0xff0000);
});

 全部代码

//导入 threejs
import * as THREE from "three";
//导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比 窗口的宽高进行设置的
  0.1, // 近平面   相机最近最近能看到的物体
  1000 // 远平面   相机最远能看到的物体
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器的大小  (窗口大小)
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的dom元素添加到body中
document.body.appendChild(renderer.domElement);
camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);

//添加世界坐标辅助器  (红色x轴,绿色y轴,蓝色z轴)一个线段 参数为 线段长度
const axesHelper = new THREE.AxesHelper(5);
//添加到场景之中
scene.add(axesHelper);

// 添加轨道控制器 (修改侦听位置)  一般监听画布的事件  不监听document.body
const controls = new OrbitControls(camera, renderer.domElement);

//渲染函数
function animate() {
  controls.update();
  //请求动画帧
  requestAnimationFrame(animate);
  //渲染
  renderer.render(scene, camera);
}
animate();
//渲染

// 监听窗口的变化 重新设置渲染器的大小 画布自适应窗口
window.addEventListener("resize", () => {
  // 重新设置渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 重新设置相机的宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 重新计算相机的投影矩阵
  camera.updateProjectionMatrix();
});

// 创建三个球
const sphere1 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x00ff00,
  })
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0x0000ff,
  })
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),
  new THREE.MeshBasicMaterial({
    color: 0xff00ff,
  })
);
sphere3.position.x = 4;
scene.add(sphere3);

// 创建射线
const raycaster = new THREE.Raycaster();

//创建鼠标向量
const mouse = new THREE.Vector2();

//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {
  console.log(e.clientX, e.clientY);
  //设置鼠标向量的xy值

  //设置鼠标向量的xy值
  //公式 可以用边框四个点做测试 或者暂时先记住
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);

  //通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera(mouse, camera);

  //计算物体和射线的焦点
  const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
  // console.log(intersects);
  if (intersects[0].object._isSelect) {
    //设定旧的颜色
    intersects[0].object.material.color.set(intersects[0].object._originColor);
    intersects[0].object._isSelect = false;
    return;
  }
  //添加一个属性 选中状态
  intersects[0].object._isSelect = true;
  //将之前的颜色记录下来
  intersects[0].object._originColor =
    intersects[0].object.material.color.getHex();
  //设定新的颜色
  intersects[0].object.material.color.set(0xff0000);
});

// //给window侦听点击事件 获取点击到的地方
// window.addEventListener("click", (e) => {
//   // console.log(e.clientX, e.clientY);
//   //设置鼠标向量的xy值
//   //公式 可以用边框四个点做测试 或者暂时先记住
//   mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
//   mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);

//   //测试向量坐标是否正确
//   // console.log(mouse.x, mouse.y);

//   //通过摄像机和鼠标位置更新射线
//   raycaster.setFromCamera(mouse, camera);

//   //计算物体和射线的焦点
//   const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);

//   if (intersects[0].object._isSelect) {
//     //设定旧的颜色
//     intersects[0].object.material.color.set(intersects[0].object._originColor);
//     intersects[0].object._isSelect = false;
//     return;
//   }
//   //添加一个属性 选中状态
//   intersects[0].object._isSelect = true;
//   //将之前的颜色记录下来
//   intersects[0].object._originColor =
//     intersects[0].object.material.color.getHex();
//   //设定新的颜色
//   intersects[0].object.material.color.set(0xff0000);
//   // console.log(intersects);
// });
//3D 光线投射技术 完成3D三维场景中事件交互

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web阿成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值