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,源码和模型


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

目录
相关文章
|
2月前
|
前端开发 JavaScript UED
element-ui 表格数据究竟隐藏着怎样的神秘样式与格式化技巧?快来揭开谜底!
【8月更文挑战第22天】《element-ui 表格数据样式及格式化案例》展示了如何利用 element-ui 的表格组件实现美观且易读的数据展示。通过简单配置,可以自定义表格样式,如边框、背景色等,并通过 formatter 实现数据格式化,例如将成绩保留一位小数。此外,还能依据条件设置行样式,如成绩达优则高亮显示,从而增强用户体验和数据可读性。
60 1
|
3月前
|
JavaScript 定位技术
vue-baidu-map 绘制行政区划的轮廓,添加行政区划名称(含给覆盖物添加点击事件)——vue 百度地图开发
vue-baidu-map 绘制行政区划的轮廓,添加行政区划名称(含给覆盖物添加点击事件)——vue 百度地图开发
234 1
|
3月前
|
JavaScript 小程序 前端开发
【微信小程序-原生开发】实用教程04-启动/欢迎/首屏广告页(含倒计时、添加文字、rpx、定义变量和函数、读取变量、修改变量、wx.reLaunch 页面跳转、生命周期 onReady等)
【微信小程序-原生开发】实用教程04-启动/欢迎/首屏广告页(含倒计时、添加文字、rpx、定义变量和函数、读取变量、修改变量、wx.reLaunch 页面跳转、生命周期 onReady等)
124 0
|
5月前
|
JavaScript 前端开发 定位技术
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)(上)
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)
60 0
|
5月前
|
JSON 定位技术 数据格式
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)(下)
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)
63 0
|
JavaScript 前端开发 定位技术
百度地图JavaScript API使用Boundary方法添加覆盖物显示省市某一区域的解决方案
百度地图JavaScript API使用Boundary方法添加覆盖物显示省市某一区域的解决方案
531 0
|
前端开发 JavaScript
引用阿里图标库的三种方式——多色图标我选symbol
引用阿里图标库的三种方式——多色图标我选symbol
421 0
|
前端开发
前端工作总结157-赋值给form对象
前端工作总结157-赋值给form对象
76 0
前端工作总结157-赋值给form对象
|
JavaScript 前端开发
#yyds干货盘点# 【js学习笔记四十四】遍历对象的属性和值
#yyds干货盘点# 【js学习笔记四十四】遍历对象的属性和值
113 0
#yyds干货盘点# 【js学习笔记四十四】遍历对象的属性和值