Threejs鼠标点击场景对象获取对象信息,Threejs使用Raycaster拾取对象信息

简介: Threejs鼠标点击场景对象获取对象信息,Threejs使用Raycaster拾取对象信息

1,介绍


该示例使用的是 r95版本Three.js库。这里实现如何使用鼠标选择场景中的对象。

效果图如下:


2,主要说明


我们使用THREE.Projector和THREE.Raycaster来检测是否使用鼠标点击了某个对象。当我们在屏幕上点击鼠标时,会发生如下的事情:

(1)首先,基于屏幕上的点击位置会创建一个THREE.Vecor3向量。

(2)接着,使用vector.unproject方法将屏幕上的点击位置转换成Three.js场景中的坐标。换句话说,就是将屏幕坐标转换成三维场景中的坐标。

(3)然后,创建THREE.Raycaster。使用THREE.Raycaster可以向场景中发射光线。在该示例中,从摄像机的位置(camera.position)向场景中鼠标的点击位置发射光线。

(4)最后,我们使用raycaster.intersectObjects方法来判断指定的对象中哪些被该光线照射到了。


document.addEventListener('click', onDocumentMouseDown, false);
function onDocumentMouseDown(event) {
  // 点击屏幕创建一个向量
  var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window
    .innerHeight) * 2 + 1, 0.5);
  vector = vector.unproject(camera); // 将屏幕的坐标转换成三维场景中的坐标
  var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
  var intersects = raycaster.intersectObjects(mesh, true);
  if (intersects.length > 0) {
    intersects[0].object.material.color.set("#ff0000");
  }
}

 


     所点击的网格是对象,face和faceIndex指的是该网格中被选中的面。distance属性是从摄像机到被点击对象的距离,point属性则表明网格上哪个点被点击了。最后,我们还可以获得点击位置所对应的2D纹理的uv值。在该示例中任何被选中的对象会变得透明,而且相关的信息会输出在浏览器的控制台上


3,源码


<!DOCTYPE html>
<html>
  <head>
    <title>Threejs鼠标点击场景对象获取对象信息,Threejs使用Raycaster拾取对象信息</title>
    <script type="text/javascript" src="libs/three.js"></script>
    <script type="text/javascript" src="libs/OrbitControls.js"></script>
    <style>
      body {
        margin: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="dom"></div>
    <script type="text/javascript">
      var camera;
      var renderer;
      var length = 36;
      var ws = 2;
      var graph = [];
      var mesh = [];
      function init() {
        // 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
        var scene = new THREE.Scene();
        var urls = [
          'assets/textures/cubemap/flowers/posx.jpg',
          'assets/textures/cubemap/flowers/negx.jpg',
          'assets/textures/cubemap/flowers/posy.jpg',
          'assets/textures/cubemap/flowers/negy.jpg',
          'assets/textures/cubemap/flowers/posz.jpg',
          'assets/textures/cubemap/flowers/negz.jpg'
        ];
        var cubeLoader = new THREE.CubeTextureLoader();
        scene.background = cubeLoader.load(urls);
        // 创建一个摄像机,它定义了我们正在看的地方
        camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 将摄像机对准场景的中心
        camera.position.x = 60;
        camera.position.y = 35;
        camera.position.z = 60;
        camera.lookAt(scene.position);
        var orbit = new THREE.OrbitControls(camera);
        // 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
        // initialize basic renderer
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 将平面添加到场景中
        var plane = createPlaneGeometryBasicMaterial();
        scene.add(plane);
        // 在屏幕上显示坐标轴
        var axes = new THREE.AxesHelper(100);
        scene.add(axes);
        scene.add(new THREE.AmbientLight(0x666666));
        scene.add(new THREE.AmbientLight("#ffffff", 1));
        document.getElementById("dom").appendChild(renderer.domElement);
        initGround();
        initGrid();
        // 启动动画
        renderScene();
        // 创建一个地面
        function createPlaneGeometryBasicMaterial() {
          var textureLoader = new THREE.TextureLoader();
          var cubeMaterial = new THREE.MeshStandardMaterial({
            map: textureLoader.load("assets/textures/stone/cd.jpg"),
          });
          cubeMaterial.map.wrapS = THREE.RepeatWrapping;
          cubeMaterial.map.wrapT = THREE.RepeatWrapping;
          cubeMaterial.map.repeat.set(18, 18)
          // 创建地平面并设置大小
          var planeGeometry = new THREE.PlaneGeometry(500, 500);
          var plane = new THREE.Mesh(planeGeometry, cubeMaterial);
          // 设置平面位置并旋转
          plane.rotation.x = -0.5 * Math.PI;
          plane.position.x = 0;
          plane.position.z = 0;
          return plane;
        }
        // 初始化线路
        function initLine(pArr) {
          var points = [];
          var geometry = new THREE.Geometry();
          for (var i = 0; i < pArr.length; i++) {
            var randomX = pArr[i].x;
            var randomY = pArr[i].y;
            var randomZ = pArr[i].z;
            var vector = new THREE.Vector3(randomX, randomY, randomZ);
            geometry.vertices.push(vector);
            points.push(vector);
          }
          var material = new THREE.LineBasicMaterial({
            color: 0x0000FF
          });
          var line = new THREE.Line(geometry, material);
          scene.add(line);
          return points;
        }
        // 绘制路网
        function initGround() {
          var geometry = new THREE.Geometry();
          geometry.vertices.push(new THREE.Vector3(0, 0, 0));
          geometry.vertices.push(new THREE.Vector3(length, 0, 0));
          for (var i = 0; i <= length / ws; i++) {
            var material = new THREE.LineBasicMaterial({
              color: 0x808080
            });
            var line = new THREE.Line(geometry, material);
            line.position.z = i * ws;
            scene.add(line);
            var line = new THREE.Line(geometry, material);
            line.position.x = i * ws;
            line.position.z = length;
            line.rotation.y = 90 * Math.PI / 180;
            scene.add(line);
          }
        }
        // 初始化障碍物
        function initGrid() {
          for (var i = 0; i < length / ws; i++) {
            var nodeRow = [];
            for (var j = 0; j < length / ws; j++) {
              var salt = Math.random() * 7;
              if (salt > 2) {
                nodeRow.push(1);
              } else {
                nodeRow.push(0);
              }
              if (salt <= 2) {
                var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({
                  color: 0xC0C0C0
                }));
                let x = ws * j + ws / 2;
                let z = ws * i + ws / 2;
                cube.position.set(x, 1.2, z);
                scene.add(cube);
                mesh.push(cube);
              }
            }
            graph.push(nodeRow);
          }
        }
        document.addEventListener('click', onDocumentMouseDown, false);
        function onDocumentMouseDown(event) {
          // 点击屏幕创建一个向量
          var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window
            .innerHeight) * 2 + 1, 0.5);
          vector = vector.unproject(camera); // 将屏幕的坐标转换成三维场景中的坐标
          var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
          var intersects = raycaster.intersectObjects(mesh, true);
          if (intersects.length > 0) {
            intersects[0].object.material.color.set("#ff0000");
          }
        }
        // 动画渲染
        var step = 5;
        function renderScene() {
          orbit.update();
          // 使用requestAnimationFrame函数进行渲染
          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }
        // 渲染的场景
        renderer.render(scene, camera);
      }
      window.onload = init;
      function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      // 监听调整大小事件
      window.addEventListener('resize', onResize, false);
    </script>
  </body>
</html>

4,源码和模型


需要完整代码、模型或者其他源码,请进入博客首页查看其他文章或者留言

目录
相关文章
|
1月前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
4月前
|
图形学
小功能⭐️不用传参,Unity获得鼠标悬浮处的物体、UI!
小功能⭐️不用传参,Unity获得鼠标悬浮处的物体、UI!
|
前端开发 定位技术 API
前端切图:调用百度地图API
前端切图:调用百度地图API
69 0
|
JavaScript 前端开发 定位技术
百度地图JavaScript API使用Boundary方法添加覆盖物显示省市某一区域的解决方案
百度地图JavaScript API使用Boundary方法添加覆盖物显示省市某一区域的解决方案
555 0
|
小程序 定位技术 API
小程序地图学习之获取位置 获取经纬度 获取地名 获取地址
小程序地图学习之获取位置 获取经纬度 获取地名 获取地址
202 0
|
Web App开发 数据采集 JavaScript
一日一技:在网页上如何获取鼠标当前指向的元素
一日一技:在网页上如何获取鼠标当前指向的元素
453 0
|
JavaScript 前端开发 编译器
HTML调用百度地图API,实现地图标注位置
HTML调用百度地图API,实现地图标注位置
572 0
|
JavaScript
JS案例:小球拖动,记录轨迹,并原路返回
JS案例:小球拖动,记录轨迹,并原路返回
144 0
|
前端开发
好客租房30-事件绑定this指向(箭头函数)
好客租房30-事件绑定this指向(箭头函数)
84 0
|
缓存 前端开发 JavaScript
浅谈three.js 中得needsUpdate属性
为什么需要needsUpdate 首先还是来看下为什么需要缓存,缓存的存在一般都是为了减少数据传输的次数,从而减少程序在数据传输上消耗的时间,这里也是,一般一个物体(Mesh)要最后能够成功显示到屏幕前是很不容易的,需要转三次战场 首先是通过程序将所有的顶点数据和纹理数据从本地磁盘读取到内存当中。 然后程序在内存中做了适当的处理之后就要将那些需要绘制到屏幕前的物体的顶点数据和纹理数据传输到显存当中。 最后在每一帧渲染的时候将显存中的顶点数据和纹理数据flush到GPU中进行装配,绘制。 根据那个金字塔式的数据传输模型,第一步显然是最慢的,如果是在WebGL这样的环境中通过网络来传输,那就更