javascript - 使用 raycaster 选择 gltf 的特定元素并添加事件监听器

标签 javascript three.js

我正在构建一个交互式网络应用程序。我已经将 glb 文件加载到场景中,现在我想添加 2 个功能:

  1. onMouseover 在对象的其中一个元素上我想更改该元素的颜色。
  2. 3D 对象由一个 Texture_Group[Object3D] 组成,如下面的控制台日志所示。纹理组本质上是拼出 HAJDUK 的 3D 字母(对象),但我将它们导入为分组的 .glb 文件,我想添加一个事件监听器,因此当单击单个字母(对象)时会加载不同的链接对于每个单独的对象。

我的困惑是,我是否可以仅通过使用 raycaster 以某种方式做到这一点,或者我是否需要进一步遍历 Texture_Group 并为此开发一种 if else 逻辑,以及如何实现这些逻辑?
我猜我需要使用 raycaster 并遍历场景,但我有点卡住了。感谢您的帮助,如果我现在需要提供任何其他信息,请告诉我,这是我的第一个问题。

我在网上找到的一些代码的帮助下遍历了 console.log 中的场景,并根据我的情况对其进行了一些调整。

这是我的 app.js 代码:

/*jshint esversion: 6 */
let scene, camera, renderer, h, controls, raycaster, mouse;

function init() {
  scene = new THREE.Scene();

  //Camera
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(0, 6, 6);

  const myCanvas = document.getElementById('myCanvas');

  //Renderer
  renderer = new THREE.WebGLRenderer({
    canvas: myCanvas,
    antialias: true,
  });
  renderer.setClearColor(0xcfd4d8);
  renderer.setPixelRatio(window.deviceSPixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  //Controls
  controls = new THREE.OrbitControls(camera, myCanvas);
  controls.target.set(0, 6, 0);
  controls.update();

  controls.enableDamping = true;

  controls.dampingFactor = 0.25;

  controls.screenSpacePanning = false;

  controls.minDistance = -20;
  controls.maxDistance = 100;

  controls.maxPolarAngle = Math.PI;
  controls.enableZoom = true;

  controls.rotateSpeed = 0.3;
  controls.zoomSpeed = 10.0;

  //Lights
  const light = new THREE.AmbientLight(0xffffff, 0.5);
  scene.add(light);

  const light2 = new THREE.PointLight(0xffffff, 0.5);
  scene.add(light2);

  //Loader
  const loader = new THREE.GLTFLoader();
  loader.load(
    'content/hajdukzagltf.glb',
    function (gltf) {
      h = scene.add(gltf.scene);
      h.position.set(6, -2, 1);
      console.log(dumpObject(h).join('\n'));
      console.log(gltf);
    }
  );

}

init();

//Render Loop
render();

function render() {

  requestAnimationFrame(render);

  controls.update();
  renderer.render(scene, camera);
}

//windowResize
function windowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

window.addEventListener('resize', windowResize, false);

//consol log traverse
function dumpVec3(v3, precision = 3) {
  return `${v3.x.toFixed(precision)}, ${v3.y.toFixed(precision)}, ${v3.z.toFixed(precision)}`;
}

function dumpObject(obj, lines = [], isLast = true, prefix = '') {
  const localPrefix = isLast ? '└─' : '├─';
  lines.push(`${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]`);
  const dataPrefix = obj.children.length
     ? (isLast ? '  │ ' : '│ │ ')
     : (isLast ? '    ' : '│   ');
  lines.push(`${prefix}${dataPrefix}  pos: ${dumpVec3(obj.position)}`);
  lines.push(`${prefix}${dataPrefix}  rot: ${dumpVec3(obj.rotation)}`);
  lines.push(`${prefix}${dataPrefix}  scl: ${dumpVec3(obj.scale)}`);
  const newPrefix = prefix + (isLast ? '  ' : '│ ');
  const lastNdx = obj.children.length - 1;
  obj.children.forEach((child, ndx) => {
    const isLast = ndx === lastNdx;
    dumpObject(child, lines, isLast, newPrefix);
  });

  return lines;
}

控制台日志:

*no-name* [Scene]
  │   pos: 6.0000, -2.0000, 1.0000
  │   rot: 0.0000, 0.0000, 0.0000
  │   scl: 1.0000, 1.0000, 1.0000
  ├─*no-name* [AmbientLight]
  │     pos: 0.0000, 0.0000, 0.0000
  │     rot: 0.0000, 0.0000, 0.0000
  │     scl: 1.0000, 1.0000, 1.0000
  ├─*no-name* [PointLight]
  │     pos: 0.0000, 0.0000, 0.0000
  │     rot: 0.0000, 0.0000, 0.0000
  │     scl: 1.0000, 1.0000, 1.0000
  └─*no-name* [Scene]
    │   pos: 0.0000, 0.0000, 0.0000
    │   rot: 0.0000, 0.0000, 0.0000
    │   scl: 1.0000, 1.0000, 1.0000
    ├─Camera_(2) [PerspectiveCamera]
    │     pos: -12.5626, 8.9117, -9.9300
    │     rot: -0.1273, -0.2253, -0.0286
    │     scl: 1.0000, 1.0000, 1.0000
    ├─Texture_Group [Object3D]
    │     pos: 0.0000, 0.0000, 0.0000
    │     rot: 0.0000, 0.0000, 0.0000
    │     scl: 1.0000, 1.0000, 1.0000
    ├─b [Mesh]
    │     pos: 2.6200, 3.9200, -19.2000
    │     rot: 0.0000, 0.0000, 0.0000
    │     scl: 1.0000, 1.0000, 1.0000
    ├─z [Mesh]
    │     pos: 0.0000, 0.0000, 0.0000
    │     rot: 0.0000, 0.0000, 0.0000
    │     scl: 1.0000, 1.0000, 1.0000
    └─bg [Mesh]
          pos: 0.0000, 0.0000, 0.0000
          rot: 0.0000, 0.0000, 0.0000
          scl: 1.0000, 1.0000, 1.0000

最佳答案

如评论部分所述,所有功能都可以使用简单的 Raycaster 实现。 .

为了检测鼠标何时悬停在场景中的一组对象上,您可以将它们分组到包含子对象的 THREE.GroupTHREE.Object3D 中网格。

您将 mousemove 事件绑定(bind)到更新包含视口(viewport)鼠标位置的全局变量 mouse 的函数。

function onMouseMove( event ) {

  event.preventDefault();

  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

}

在你的动画循环中,你执行给定当前鼠标位置的光线转换,并检查它是否与所述组包含的任何对象相交。然后,您需要处理更新这些对象的颜色或在不相交时恢复这些对象的颜色。

function raycast() {

  raycaster.setFromCamera( mouse, camera );

  var intersects = raycaster.intersectObjects( group.children );

  if ( intersects.length > 0 ) {

    if ( INTERSECTED != intersects[ 0 ].object ) {

      if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );

      INTERSECTED = intersects[ 0 ].object;
      INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
      INTERSECTED.material.color.setHex( 0xd4d4d4 );

    }

  } else {

    if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );

    INTERSECTED = null;

  }

}

最后,如果对象当前与射线相交,则将 click 事件绑定(bind)到另一个执行所需功能的函数。

function onMouseClick( event ) {

  if ( INTERSECTED !== null ) clickFunction( INTERSECTED ); // perform object operation

}

JSFiddle example显示这些功能的工作。

如果有什么不清楚的地方,请告诉我。


如果 TEXTURE_GROUP 中的对象没有整齐地组织在网格中,我建议您首先将每个字母打包到一个网格中,然后在遍历时将它们添加到 Object3D Group 如果它们还没有在一个中。

关于javascript - 使用 raycaster 选择 gltf 的特定元素并添加事件监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56317214/

相关文章:

javascript - 如何从json中获取输入来绘制图形

javascript - 如何修改 Backbone 同步期间生成的 URL,但仅限于特定请求

javascript - ng-click 不适用于 btn 元素

javascript - 用户个人资料显示未正确使用 ajax

javascript - Three.js改变网格纹理改变了整个模型

javascript - 如何在 three.js 中切割现有几何体

three.js - 什么是 Three.js 的 CopyShader?

javascript - Three.js 3d sphere looks 2d

javascript - aframe raycaster 可以定制成这样吗

javascript - 从使用 getElementsByClassName() 创建的数组中获取值