javascript - 如何在 THREE.js 中实现精灵在球体表面上的均匀分布?
问题描述
我正在尝试创建一个单词数据库,其中最重要的单词更靠近球体的顶部,而不太重要的单词则离球体更远。因此,我为每个单词创建了一个具有足够顶点的球体,按照与球体顶部的距离顺序创建了这些顶点的列表,并将文本精灵按排序列表的顺序放置在顶点的位置。
视频版:https ://i.gyazo.com/aabaf0b4a26f4413dc6a0ebafab2b4bd.mp4
在我的脑海中听起来像是一个很好的计划,但显然球体的几何形状会导致单词离顶部越远越分散。我需要一个表面看起来有点均匀分布的结果。它不必是完美的,只是视觉上比这更接近。
我怎样才能达到预期的效果?
以下是相关方法:
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
});
}
});
解决方案
也许是斐波那契球体
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 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>
推荐阅读
- html - 不能垂直排列 div 内的所有元素(1 个图像 2 个跨度),2 个跨度应该在彼此之上
- asp.net - 在 ASP.NET Core 用户请求中读取 Azure AD 用户属性
- python - Python CLI 应用程序在从 PyPI 安装时不运行,在本地安装时运行良好
- excel - 如何使用 Excel 中另一列的条件计算一列中的单元格值?
- java - 带有条件内容的绑定标签?
- html - 在一定的页面宽度之后,我的两个 DIV 正在离开页面
- java - 使用Java在窗口中水平居中文本
- android - getAssets() 在 MainActivity.class 以外的其他类中无法识别
- linux - 在 CentOS 上为 Linux 内核 4.14 安装实时内核
- android - 如何在我自己的 Android 库中使用 Android 支持库