A-Frame:将一个实体保持在另一个实体前面并面向相机

A-Frame: keep one entity in front of another and facing camera

我想定位和旋转实体(例如 A 型框架 text entity) so that it is always just in front of another (e.g. a box), i.e. the text is positioned slightly in front of the box from the point of view of the camera, facing the camera. I can use the look-at 组件以使文本面向相机,但我不知道如何定位文本。我需要一个通用解决方案,因为框的大小和位置可能不同,所以硬编码文本的位置将不起作用。

目标是在方框前面显示工具提示之类的东西(一旦我有了基本的想法,我就会做到这一点,只有当您将鼠标悬停在方框上时,文本才会出现)。

有多种方法可以做到这一点,这里有两个想法:

1。将 HTML 元素与 3D 对象对齐

This is an amazing resource which is worth checking out (as is the entire Fundamentals manual),我在下面用到了(经过简化,希望这样思路更清晰)

想法很简单:

  • 获取对象的世界位置
  • 将其映射到“屏幕”(x,y) 位置
  • 用 css 定位一个 <p> 元素,使用上面的 x/y 值:

p {
  z-index: 10;
  position: absolute;
  /* let us position them inside the container */
  left: 0;
  /* make their default position the top left of the container */
  top: 0;
  cursor: pointer;
  /* change the cursor to a hand when over us */
  font-size: large;
  user-select: none;
  /* don't let the text get selected */
}
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script>
  AFRAME.registerComponent("foo", {
    schema: {text: {default: ""}},
    init: function() {
       // grab the div, which will contain all <p> elements
       const layoutParent = document.querySelector("#overlays")
       // wait until loaded
       this.el.addEventListener("loaded", evt => { 
        const layoutEl = document.createElement("p") // create the overlay element
        layoutEl.innerHTML = this.data.text          // set the inner text
        layoutParent.appendChild(layoutEl)           // append to the parent
        
        // keep references to use in the "tick" function
        this.layoutEl = layoutEl    
        this.mesh = this.el.getObject3D("mesh")
       })
       this.tmpV = new THREE.Vector3(0,0,0); // for later use
    },
    tick: function() {
      // ignore if there is no mesh/text yet
      if (!this.mesh || !this.layoutEl) return;
      const tmpV = this.tmpV;
      const canvas = this.el.sceneEl.canvas
      const camera = this.el.sceneEl.camera
      // get the world position
      this.mesh.getWorldPosition(tmpV);
      // get the normalized screen coordinate of that position
      tmpV.project(camera);
      // convert the normalized position to CSS coordinates
      const x = (tmpV.x *  .5 + .5) * canvas.clientWidth;
      const y = (tmpV.y * -.5 + .5) * canvas.clientHeight;
      // move the elem to that position
      this.layoutEl.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
    }
  })
</script>
<div id="overlays">
</div>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" 
         foo="text: some overlay"></a-box>
</a-scene>

希望评论说清楚 - 虽然你真的应该检查 manual

2。使用 3D 文本

您可以计算 box -> camera 向量,并使用它来偏移叠加文本 - 结合 lookAt 应该会得到您正在寻找的效果。

相同想法的更简单方法是:

  • 创建一个虚拟装备,它将使用 look-at
  • 跟随相机
  • 在钻机中放置一个 a-text,将其 z 偏移框边界球体半径。

<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
<script>
  AFRAME.registerComponent("spherical-tooltip", {
    schema: {
      text: {
        default: ""
      }
    },
    init: function() {
      this.el.addEventListener("loaded", evt => {
        const mesh = this.el.getObject3D("mesh")
        // the timeout is an ugly hack,
        // the bounding sphere isn't set yet
        setTimeout(evt => {
          const bsphere = mesh.geometry.boundingSphere
          // the main rig
          const rig = document.createElement("a-entity");
          rig.setAttribute("look-at", "[camera]")
          this.el.appendChild(rig)

          // setup the text, and offset the position
          const tooltip = document.createElement("a-text");
          tooltip.setAttribute("color", "black")
          tooltip.setAttribute("value", this.data.text)
          tooltip.setAttribute("align", "center")
          tooltip.setAttribute("position", {x: 0, y: 0, z: bsphere.radius})
          rig.appendChild(tooltip)
        }, 150)
      })
    }
  })
</script>
<a-scene>
  <a-box spherical-tooltip="text: tooltip" position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
</a-scene>