簡體   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