How can I achieve an even distribution of sprites across the surface of a sphere in THREE.js?

Viziionary :

I'm trying to make a database of words where the most important words are closer to the top of the sphere and the less important are further away. So I created a sphere with enough vertices for each word, created a list of those vertices in order of distance from the top of the sphere, and placed the text sprites at the positions of the vertices in order of that sorted list.

enter image description here Video version: https://i.gyazo.com/aabaf0b4a26f4413dc6a0ebafab2b4bd.mp4

Sounded like a good plan in my head, but clearly the geometry of a sphere causes the words to be further spread out the further away from the top they are. I need a result that looks like a somewhat even distribution across the surface. It doesn't have to be perfect, just visually closer than this.

How can I achieve the desired effect?

Here are the relevant methods:

positionDb(db) {
    console.log("mostRelated", db.mostRelated);
    console.log("depthList", this.depthList);
    let mostRelated = db.mostRelated;
    let depthList = this.depthList;
    for (let i = 0; i < mostRelated.length; i++) {
      this.addTextNode(mostRelated[i].data, this.depthList[i].vertice, this.depthList[i].depth);
    }

}
addTextNode(text, vert, distance) {
    let fontSize = 0.5 * (600 / distance);
    let sprite = new THREE.TextSprite({
      fillStyle: '#000000',
      fontFamily: '"Arial", san-serif',
      fontSize: fontSize,
      fontWeight: 'bold',
      text: text
    });
    this.scene.add(sprite);
    sprite.position.set(vert.x, vert.y, vert.z);
    setTimeout(() => {
        sprite.fontFamily = '"Roboto", san-serif';
    }, 1000)
}


this.scene = scene;
this.geometry = new THREE.SphereGeometry(420, 50, 550);
var material = new THREE.MeshBasicMaterial({
    color: 0x0011ff
});
var sphere = new THREE.Mesh(this.geometry, wireframe);
var wireframe = new THREE.WireframeGeometry(this.geometry);
let frontVert = {
    x: 0,
    y: 100,
    z: 0
}
let depthList = [];
this.geometry.vertices.forEach(vertice => {
  let depth = getDistance(frontVert, vertice);
  if (depthList.length === 0) {
    depthList.push({
      depth,
      vertice
    });
  } else {
    let flag = false;
    for (let i = 0; i < depthList.length; i++) {
      let item = depthList[i];
      if (depth < item.depth) {
        flag = true;
        depthList.splice(i, 0, {
          depth,
          vertice
        });
        break;
      }
    }
    if (!flag) depthList.push({
      depth,
      vertice
    });
  }
});
gman :

Maybe a fibonacci sphere

function fibonacciSphere(numPoints, point) {
  const rnd = 1;
  const offset = 2 / numPoints;
  const increment = Math.PI * (3 - Math.sqrt(5));

  const y = ((point * offset) - 1) + (offset / 2);
  const r = Math.sqrt(1 - Math.pow(y, 2));

  const phi = (point + rnd) % numPoints * increment;

  const x = Math.cos(phi) * r;
  const z = Math.sin(phi) * r;

  return new THREE.Vector3(x, y, z);
}

Example:

function fibonacciSphere(numPoints, point) {
  const rnd = 1;
  const offset = 2 / numPoints;
  const increment = Math.PI * (3 - Math.sqrt(5));

  const y = ((point * offset) - 1) + (offset / 2);
  const r = Math.sqrt(1 - Math.pow(y, 2));

  const phi = (point + rnd) % numPoints * increment;

  const x = Math.cos(phi) * r;
  const z = Math.sin(phi) * r;

  return new THREE.Vector3(x, y, z);
}

function main() {
  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2;

  const scene = new THREE.Scene();

  function addTextNode(text, vert) {
    const div = document.createElement('div');
    div.className = 'label';
    div.textContent = text;
    div.style.marginTop = '-1em';
    const label = new THREE.CSS2DObject(div);
    label.position.copy(vert);
    scene.add(label);
  }

  const renderer = new THREE.CSS2DRenderer();
  const container = document.querySelector('#c');
  container.appendChild(renderer.domElement);

  const controls = new THREE.OrbitControls(camera, renderer.domElement);
  
  const numPoints = 50;
  for (let i = 0; i < numPoints; ++i) {
    addTextNode(`p${i}`, fibonacciSphere(numPoints, i));
  }

  function render(time) {
    time *= 0.001;

    // three's poor choice of how to hanlde size strikes again :(
    renderer.setSize(container.clientWidth, container.clientHeight); 
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
  overflow: hidden;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
.label {
  color: red;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/examples/js/renderers/CSS2DRenderer.js"></script>
<div id="c"></div>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=32010&siteId=1