我怎样才能将文字环绕在 3D 球体周围

How can i wrap a text arround 3D sphere

我正在寻找一种在 babylon 或 threejs 中将文本环绕在球体周围的方法。我对改变 javascript 技术

持开放态度

我会看看 an example of generating text。然后我会分别生成每个字母,记录它们各自的宽度,并使用它们来计算我想要显示的字符串的总宽度

然后我可以将每个网格作为 Object3D 的父级,并将 Object3D 的旋转 y 设置为

widthSoFar = 0;
for each letter
   obj3d.rotation.y = widthSoFar / totalWidth * Math.PI * 2;
   widthSoFar += widthOfCurrentLetter;

并将字母的 position.z 设置为某个半径,使字母围绕一个圆。

什么半径?

circumference = 2 * PI * radius

所以

radius = circumference / (2 * PI)

我们知道我们需要的周长,它是字符串的总宽度。

您可能会发现 this tutorial 有助于理解如何使用场景图节点(如 Object3D 节点)组织场景以满足您的需要。

'use strict';

/* global THREE */

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const fov = 40;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 1000;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 70;

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('black');

  function addLight(...pos) {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(...pos);
    scene.add(light);
  }
  addLight(-4, 4, 4);
  addLight(5, -4, 4);

  const lettersTilt = new THREE.Object3D();
  scene.add(lettersTilt);
  lettersTilt.rotation.set(
     THREE.Math.degToRad(-15),
     0,
     THREE.Math.degToRad(-15));
  const lettersBase = new THREE.Object3D();
  lettersTilt.add(lettersBase);
  {
    const letterMaterial = new THREE.MeshPhongMaterial({
      color: 'red',
    });  
    const loader = new THREE.FontLoader();
    loader.load('https://threejsfundamentals.org/threejs/resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {
      const spaceSize = 1.0;
      let totalWidth = 0;
      let maxHeight = 0;
      const letterGeometries = {
        ' ': { width: spaceSize, height: 0 }, // prepopulate space ' '
      };
      const size = new THREE.Vector3();
      const str = 'threejs fundamentals ';
      const letterInfos = str.split('').map((letter, ndx) => {
        if (!letterGeometries[letter]) {
          const geometry = new THREE.TextBufferGeometry(letter, {
            font: font,
            size: 3.0,
            height: .2,
            curveSegments: 12,
            bevelEnabled: true,
            bevelThickness: 0.5,
            bevelSize: .3,
            bevelSegments: 5,
          });
          geometry.computeBoundingBox();
          geometry.boundingBox.getSize(size);
          letterGeometries[letter] = {
            geometry,
            width: size.x / 2, // no idea why size.x is double size
            height: size.y,
          };
        }
        const {geometry, width, height} = letterGeometries[letter];
        const mesh = geometry
            ? new THREE.Mesh(geometry, letterMaterial)
            : null;
        totalWidth += width;
        maxHeight = Math.max(maxHeight, height);
        return {
          mesh,
          width,
        };
      });
      let t = 0;
      const radius = totalWidth / Math.PI;
      for (const {mesh, width} of letterInfos) {
        if (mesh) {
          const offset = new THREE.Object3D();
          lettersBase.add(offset);
          offset.add(mesh);
          offset.rotation.y = t / totalWidth * Math.PI * 2;
          mesh.position.z = radius;
          mesh.position.y = -maxHeight / 2;
        }
        t += width;
      }
      {
        const geo = new THREE.SphereBufferGeometry(radius - 1, 32, 24);
        const mat = new THREE.MeshPhongMaterial({
         color: 'cyan',
        });
        const mesh = new THREE.Mesh(geo, mat);
        scene.add(mesh);
      }
      camera.position.z = radius * 3;
    });
  }

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    lettersBase.rotation.y = time * -0.5;

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<canvas id="c"></canvas>
  <script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>