我正在寻找一种更好/更快的方法来找到我的PlaneBufferGeometry
中的相邻面孔(具有相同的边缘)。目前,我的THREE.Raycaster
与我的对象很好相交(使用intersectObject
)。但是我需要找到所有周围的面孔。
目前,我使用以下肮脏的方法来查找“隔壁”的面孔。它在我的情况下效果很好,但感觉不正确:
let rc = new THREE.Raycaster();
let intersects = [];
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
let v = new THREE.Vector3(x + i, y, z + j);
rc.set(v, new THREE.Vector3(0, -1, 0));
rc.near = 0;
rc.far = 2;
let subIntersects = rc.intersectObject(mesh);
for (let n = 0; n < subIntersects.length; n++) {
intersects.push(subIntersects[n]);
}
}
}
例如,是否有一种方法可以在mesh.geometry.attributes.position.array
中快速找到这些面孔?欢迎任何建议。预先感谢!
您提到了位置数组中的5万个商品。在我看来,这似乎并不慢。这个示例只有10x10,所以数组中有100个项目,因此很容易看到它正在工作,但是我将其设置为200x200,这是40k个项目,而且看起来足够快了吗?
'use strict';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 60;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 200;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 1;
const scene = new THREE.Scene();
scene.background = new THREE.Color('#444');
scene.add(camera);
const planeGeometry = new THREE.PlaneBufferGeometry(1, 1, 20, 20);
const material = new THREE.MeshBasicMaterial({color: 'blue'});
const plane = new THREE.Mesh(planeGeometry, material);
scene.add(plane);
const edgeGeometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
edgeGeometry.setAttribute('position', planeGeometry.getAttribute('position'));
edgeGeometry.setIndex([]);
const edgeMaterial = new THREE.MeshBasicMaterial({
color: 'yellow',
wireframe: true,
depthTest: false,
});
const edges = new THREE.Mesh(edgeGeometry, edgeMaterial);
scene.add(edges);
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;
}
class PickHelper {
constructor() {
this.raycaster = new THREE.Raycaster();
}
pick(normalizedPosition, scene, camera, time) {
// cast a ray through the frustum
this.raycaster.setFromCamera(normalizedPosition, camera);
// get the list of objects the ray intersected
const intersectedObjects = this.raycaster.intersectObjects(scene.children, [plane]);
if (intersectedObjects.length) {
// pick the first object. It's the closest one
const intersection = intersectedObjects[0];
const faceIndex = intersection.faceIndex;
const indexAttribute = planeGeometry.getIndex();
const indices = indexAttribute.array;
const vertIds = indices.slice(faceIndex * 3, faceIndex * 3 + 3);
const neighbors = []; // note: self will be added to list
for (let i = 0; i < indices.length; i += 3) {
for (let j = 0; j < 3; ++j) {
const p0Ndx = indices[i + j];
const p1Ndx = indices[i + (j + 1) % 3];
if ((p0Ndx === vertIds[0] && p1Ndx === vertIds[1]) ||
(p0Ndx === vertIds[1] && p1Ndx === vertIds[0]) ||
(p0Ndx === vertIds[1] && p1Ndx === vertIds[2]) ||
(p0Ndx === vertIds[2] && p1Ndx === vertIds[1]) ||
(p0Ndx === vertIds[2] && p1Ndx === vertIds[0]) ||
(p0Ndx === vertIds[0] && p1Ndx === vertIds[2])) {
neighbors.push(...indices.slice(i, i + 3));
break;
}
}
}
const edgeIndices = edgeGeometry.getIndex();
edgeIndices.array = new Uint16Array(neighbors);
edgeIndices.count = neighbors.length;
edgeIndices.needsUpdate = true;
}
}
}
const pickPosition = {x: 0, y: 0};
const pickHelper = new PickHelper();
clearPickPosition();
function render(time) {
time *= 0.001; // convert to seconds;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
pickHelper.pick(pickPosition, scene, camera, time);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function getCanvasRelativePosition(event) {
const rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
}
function setPickPosition(event) {
const pos = getCanvasRelativePosition(event);
pickPosition.x = (pos.x / canvas.clientWidth ) * 2 - 1;
pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1; // note we flip Y
}
function clearPickPosition() {
// unlike the mouse which always has a position
// if the user stops touching the screen we want
// to stop picking. For now we just pick a value
// unlikely to pick something
pickPosition.x = -100000;
pickPosition.y = -100000;
}
window.addEventListener('mousemove', setPickPosition);
window.addEventListener('mouseout', clearPickPosition);
window.addEventListener('mouseleave', clearPickPosition);
window.addEventListener('touchstart', (event) => {
// prevent the window from scrolling
event.preventDefault();
setPickPosition(event.touches[0]);
}, {passive: false});
window.addEventListener('touchmove', (event) => {
setPickPosition(event.touches[0]);
});
window.addEventListener('touchend', clearPickPosition);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.js"></script>
<canvas id="c"></canvas>
就像我在评论中提到的那样。如果您知道它是PlaneBufferGeometry
,则可以查看three.js代码,并查看人脸的确切布局,因此,给定faceIndex后,您可以直接计算邻居。上面的代码是通用的,至少对于具有索引的BufferGeometry而言。
如果上面的代码太慢,您也可以预先生成一个faceIndexs到邻居的映射。