从鼠标位置和深度图计算3D点

时间:2017-05-22 20:06:12

标签: three.js raycasting depth-buffer projection-matrix

我需要使用渲染的深度图从屏幕空间位置计算3D坐标。不幸的是,使用常规光线追踪对我来说不是一个选择,因为我正在处理包含大约5M面孔的单个几何体。

所以我想我会做以下事情:

  • 使用RGBADepthPacking将深度贴图渲染为renderTarget
  • 使用常规的unproject-call来计算鼠标位置的光线(就像使用光线投射时一样)
  • 从鼠标坐标深度图中查找深度,并使用该距离计算沿光线的点。

This kind of works,但不知何故,定位点总是略微落后于物体,所以我的深度计算可能有问题。

现在有关上述步骤的一些细节

渲染深度贴图非常简单:

<template>
  <div class="demo-container"></div>
</template>

<script>
  import * as d3 from 'd3';
  d3.select('.demo-container');
</script>

使用以下符号查找鼠标位置存储的颜色值:

const depthTarget = new THREE.WebGLRenderTarget(w, h);
const depthMaterial = new THREE.MeshDepthMaterial({
  depthPacking: THREE.RGBADepthPacking
});

// in renderloop
renderer.setClearColor(0xffffff, 1);
renderer.clear();
scene.overrideMaterial = depthMaterial;
renderer.render(scene, camera, depthTarget);

使用(改编自packing.glsl中的GLSL-Version)转换回浮点数:

renderer.readRenderTargetPixels(
  depthTarget, x, h - y, 1, 1, rgbaBuffer
);

最后计算深度值(我在移植到JS的const v4 = new THREE.Vector4() const unpackDownscale = 255 / 256; const unpackFactors = new THREE.Vector4( unpackDownscale / (256 * 256 * 256), unpackDownscale / (256 * 256), unpackDownscale / 256, unpackDownscale ); function unpackRGBAToDepth(rgbaBuffer) { return v4.fromArray(rgbaBuffer) .multiplyScalar(1 / 255) .dot(unpackFactors); } 中的readDepth()中找到相应的代码):

examples/js/shaders/SSAOShader.js

现在,由于此函数返回范围为0..1的值,我认为它是剪辑空间坐标中的深度,因此我将它们转换为&#34; real&#34;单位使用:

function computeDepth() {
  const cameraFarPlusNear = cameraFar + cameraNear;
  const cameraFarMinusNear = cameraFar - cameraNear;
  const cameraCoef = 2.0 * cameraNear;

  let z = unpackRGBAToDepth(rgbaBuffer);
  return cameraCoef / (cameraFarPlusNear - z * cameraFarMinusNear);
}

这些计算显然有点微不足道,我没有弄清楚有关深度存储的数学和细节。 有人可以指出我犯的错误吗?

另外:我试过的其他事情

首先我认为应该可以在我的unproject-call中使用解压缩的深度值作为z的值,如下所示:

const depth = camera.near + depth * (camera.far - camera.near);

但是,这也不能使坐标正确。

[编辑1 2017-05-23 11:00CEST]

根据@WestLangleys评论,我发现const x = mouseX/w * 2 - 1; const y = -mouseY/h * 2 + 1; const v = new THREE.Vector3(x, y, depth).unproject(camera); 功能听起来应该有所帮助。写在JS中,函数是

perspectiveDepthToViewZ()

然而,当使用深度图中的解压缩值调用时,结果会减少几个数量级。 See here

1 个答案:

答案 0 :(得分:4)

好的,好的。终于解决了。因此,对于遇到类似问题的每个人来说,解决方案都是:

computeDepth函数的最后一行是错误的。 perspectiveDepthToViewZ中有一个函数packing.glsl,很容易转换为JS:

function perspectiveDepthToViewZ(invClipZ, near, far) {
  return (near * far) / ((far - near) * invClipZ - far);
}

(我相信这在某种程度上是逆投影矩阵的一部分)

function computeDepth() {
  let z = unpackRGBAToDepth(rgbaBuffer);
  return perspectiveDepthToViewZ(z, camera.near, camera.far);
}

现在,这将返回该点的视图空间中的z轴值。要做的就是将其转换回世界空间坐标:

const setPositionFromViewZ = (function() {
  const viewSpaceCoord = new THREE.Vector3();
  const projInv = new THREE.Matrix4();

  return function(position, viewZ) {
    projInv.getInverse(camera.projectionMatrix);
    position
      .set(
        mousePosition.x / windowWidth * 2 - 1,
        -(mousePosition.y / windowHeight) * 2 + 1,
        0.5
      )
      .applyMatrix4(projInv);

    position.multiplyScalar(viewZ / position.z);
    position.applyMatrix4(camera.matrixWorld);
  };
}) ();