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


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

目录
相关文章
|
4月前
|
图形学
小功能⭐️不用传参,Unity获得鼠标悬浮处的物体、UI!
小功能⭐️不用传参,Unity获得鼠标悬浮处的物体、UI!
|
7月前
|
JSON 定位技术 数据格式
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)(下)
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)
71 0
|
7月前
|
JavaScript 前端开发 定位技术
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)(上)
HTML新特性【账号和获取密钥、初始化、变更地图类型、添加控件、改变控件位置、添加覆盖物、自定义标注图标、添加文本标注】(四)-全面详解(学习总结---从入门到深化)
65 0
|
小程序 定位技术 API
小程序地图学习之获取位置 获取经纬度 获取地名 获取地址
小程序地图学习之获取位置 获取经纬度 获取地名 获取地址
200 0
|
JavaScript 前端开发 编译器
HTML调用百度地图API,实现地图标注位置
HTML调用百度地图API,实现地图标注位置
559 0
|
JavaScript
JS案例:小球拖动,记录轨迹,并原路返回
JS案例:小球拖动,记录轨迹,并原路返回
142 0
|
JavaScript 开发者
jQuer y 特效实例_幽灵按钮2|学习笔记
快速学习 jQuery 特效实例_幽灵按钮2
262 0
jQuer y 特效实例_幽灵按钮2|学习笔记
|
缓存 前端开发 JavaScript
浅谈three.js 中得needsUpdate属性
为什么需要needsUpdate 首先还是来看下为什么需要缓存,缓存的存在一般都是为了减少数据传输的次数,从而减少程序在数据传输上消耗的时间,这里也是,一般一个物体(Mesh)要最后能够成功显示到屏幕前是很不容易的,需要转三次战场 首先是通过程序将所有的顶点数据和纹理数据从本地磁盘读取到内存当中。 然后程序在内存中做了适当的处理之后就要将那些需要绘制到屏幕前的物体的顶点数据和纹理数据传输到显存当中。 最后在每一帧渲染的时候将显存中的顶点数据和纹理数据flush到GPU中进行装配,绘制。 根据那个金字塔式的数据传输模型,第一步显然是最慢的,如果是在WebGL这样的环境中通过网络来传输,那就更
|
iOS开发
iOS开发常用方法--坐标转换
iOS开发常用方法--坐标转换
艾伟:C#3.0之自动属性和对象初始化器
  C#3.0中定义属性更加方便,不用再在像之前的版本那样的繁琐,需要先定义存储数据的字段,然后再定义属性器,现在只需要定义属性器就可以了,其它的有编译器自动为我们完成,就可以省去定义字段时需要的那些时间;在对象初始化的时候我们可在对象构造的时候实现对象属性的初始化工作,和集合初始化类似。
769 0