Three.js检测对象何时被部分和完全遮挡

问题描述 投票:1回答:2

我正在尝试检测Three.js中的一个对象何时被另一个对象部分或完全遮挡(隐藏)。

我当前的简单解决方案将一条光线投射到对象的中心:

function getScreenPos(object) {
  var pos = object.position.clone();
  camera.updateMatrixWorld();
  pos.project(camera);
  return new THREE.Vector2(pos.x, pos.y);
}

function isOccluded(object) {
  raycaster.setFromCamera(getScreenPos(object), camera);
  var intersects = raycaster.intersectObjects(scene.children);
  if (intersects[0] && intersects[0].object === object) {
    return false;
  } else {
    return true;
  }
}

但是它不考虑对象的尺寸(宽度,高度,深度)。

未被遮挡(因为物体的中心不在后面)Object not occluded

被遮挡(因为物体的中心在后面)Object occluded

查看工作演示:

https://jsfiddle.net/kmturley/nb9f5gho/57/

[目前,我可以计算对象框的大小,并为框的每个角投射光线。但这可能仍然太简单了:

var box = new THREE.Box3().setFromObject(object);
var size = box.getSize();

我想找到一种更健壮的方法,该方法可以提供partially occludedfully occluded布尔值,甚至可能是percentage occluded

javascript three.js 3d occlusion occlusion-culling
2个回答
1
投票

搜索堆栈溢出和“ GPU选择”的Three.js示例。该概念可以分为三个基本步骤:

  1. 将每种形状的材料更改为唯一的平面(MeshBasicMaterial)颜色。
  2. 使用独特的材质渲染场景。
  3. 读取渲染帧的像素以收集颜色信息。

您的情况允许您提出一些警告。

  1. 仅给出您要测试的颜色的形状-其他所有颜色都可以是黑色。
  2. 您无需渲染整个场景即可测试一种形状。您可以调整视口以仅渲染所讨论形状周围的区域。
  3. 因为您只给测试部件提供了一种颜色,所以其余数据应为零,从而使查找与您的唯一颜色匹配的像素更加容易。

现在有了像素数据,您可以确定以下内容:

  • 如果没有像素匹配唯一的颜色,则形状被完全遮挡。
  • 如果某些像素与唯一颜色匹配,则形状至少部分可见。

第二个项目符号表示该形状“至少部分”可见。这是因为您无法使用当前拥有的信息来测试完全的可见性。

I会做的(其他人可能会有更好的解决方案)是第二次渲染同一视口,但仅显示测试形状,这等效于零件完全可见。掌握了这些信息后,将像素与第一个渲染进行比较。如果两个像素都具有相同数量(也许在公差范围内)的唯一颜色像素,则可以说该零件是完全可见的/未被遮挡的。


1
投票

我根据TheJim01的答案设法获得了WebGL1的工作版本!

首先创建第二个更简单的场景用于计算:

pickingScene = new THREE.Scene();
pickingTextureOcclusion = new THREE.WebGLRenderTarget(window.innerWidth / 2, window.innerHeight / 2);
pickingMaterial = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors });
pickingScene.add(new THREE.Mesh(BufferGeometryUtils.mergeBufferGeometries([
  createBuffer(geometry, mesh),
  createBuffer(geometry2, mesh2)
]), pickingMaterial));

将对象重新创建为缓冲区几何(更快地提高性能):

function createBuffer(geometry, mesh) {
  var buffer = new THREE.SphereBufferGeometry(geometry.parameters.radius, geometry.parameters.widthSegments, geometry.parameters.heightSegments);
  quaternion.setFromEuler(mesh.rotation);
  matrix.compose(mesh.position, quaternion, mesh.scale);
  buffer.applyMatrix4(matrix);
  applyVertexColors(buffer, color.setHex(mesh.name));
  return buffer;
}

根据网眼名称添加颜色,例如id 1、2、3等)>

function applyVertexColors(geometry, color) {
  var position = geometry.attributes.position;
  var colors = [];
  for (var i = 0; i < position.count; i ++) {
    colors.push(color.r, color.g, color.b);
  }
  geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
}

然后在渲染循环中检查该纹理的第二个场景,并将像素数据与网格名称匹配:

function isOccludedBuffer(object) {
  renderer.setRenderTarget(pickingTextureOcclusion);
  renderer.render(pickingScene, camera);
  var pixelBuffer = new Uint8Array(window.innerWidth * window.innerHeight);
  renderer.readRenderTargetPixels(pickingTextureOcclusion, 0, 0, window.innerWidth / 2, window.innerHeight / 2, pixelBuffer);
  renderer.setRenderTarget(null);
  return !pixelBuffer.includes(object.name);
}

您可以在此处查看WebGL1的工作演示:

https://jsfiddle.net/kmturley/nb9f5gho/62/

这种方法要注意的一点是,您的采摘场景需要与您的主要场景的变化保持同步] >>。因此,如果您的对象移动了位置/旋转等,那么它们也需要在拾取场景中进行更新。在我的示例中,相机正在移动,而不是在移动对象,因此不需要更新。

对于WebGL2,我们将有一个更好的解决方案:

https://tsherif.github.io/webgl2examples/occlusion.html

但是目前尚不支持所有浏览器:

https://www.caniuse.com/#search=webgl

© www.soinside.com 2019 - 2024. All rights reserved.