繁体   English   中英

ThreeJS:在 PlaneBufferGeometry 中查找邻面

[英]ThreeJS: Find neighbor faces in PlaneBufferGeometry

我正在寻找一种更好/更快的方法来在我的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快速找到这些面? 欢迎任何建议。 提前致谢!

您在位置数组中提到了 50k 个项目。 这对我来说似乎不算太慢。 这个例子只有 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 是这样。

查看代码我很确定它是

// it looks like this is the grid order for PlaneBufferGeometry
//
//  b --c
//  |\\1|
//  |0\\|
//  a-- d


const facesAcrossRow = planeGeometry.parameters.widthSegments * 2;
const col = faceIndex % facesAcrossRow
const row = faceIndex / facesAcrossRow | 0;

const neighboringFaceIndices = [];

// check left face
if (col > 0) {
  neighboringFaceIndices.push(row * facesAcrossRow + col - 1);
}

// check right face
if (col < facesAcrossRow - 1) {
  neighboringFaceIndices.push(row * facesAcrossRow + col + 1);
}

// check up. there can only be one up if we're in an odd triangle (b,c,d)
if (col % 2 && row < planeGeometry.parameters.heightSegments) {
  // add the even neighbor in the next row
  neighboringFaceIndices.push((row + 1) * facesAcrossRow + col - 1);
}

// check down. there can only be one down if we're in an even triangle (a,b,d)
if (col % 2 === 0 && row > 0) {
  // add the odd neighbor in the previous row
  neighboringFaceIndices.push((row - 1) * facesAcrossRow + col + 1);
}

试试看

 '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; // it looks like this is the grid order for PlaneBufferGeometry // // b --c // |\\\\1| // |0\\\\| // a-- d const facesAcrossRow = planeGeometry.parameters.widthSegments * 2; const col = faceIndex % facesAcrossRow const row = faceIndex / facesAcrossRow | 0; const neighboringFaceIndices = []; // check left face if (col > 0) { neighboringFaceIndices.push(row * facesAcrossRow + col - 1); } // check right face if (col < facesAcrossRow - 1) { neighboringFaceIndices.push(row * facesAcrossRow + col + 1); } // check up. there can only be one up if we're in an odd triangle (b,c,d) if (col % 2 && row < planeGeometry.parameters.heightSegments) { // add the even neighbor in the next row neighboringFaceIndices.push((row + 1) * facesAcrossRow + col - 1); } // check down. there can only be one down if we're in an even triangle (a,b,d) if (col % 2 === 0 && row > 0) { // add the odd neighbor in the previous row neighboringFaceIndices.push((row - 1) * facesAcrossRow + col + 1); } const neighbors = []; for (const faceIndex of neighboringFaceIndices) { neighbors.push(...indices.slice(faceIndex * 3, faceIndex * 3 + 3)); } 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 更复杂的东西,如果顶部的代码太慢,您还可以预先生成 faceIndexs 到邻居的映射。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM