[英]Compute 3D point from mouse-position and depth-map
我需要使用渲染的深度图从屏幕空间位置计算3D坐标。 不幸的是,使用常规光线追踪对我来说不是一个选择,因为我正在处理包含大约5M面的东西的单个几何体。
所以我想我会做以下事情:
这种作品 ,但不知何故定位点总是略微落后于物体,所以我的深度计算可能有问题。
渲染深度贴图非常简单:
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);
在鼠标位置查找存储的颜色值:
renderer.readRenderTargetPixels(
depthTarget, x, h - y, 1, 1, rgbaBuffer
);
并转换回浮动使用(改编自packing.glsl中的GLSL-Version):
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);
}
最后计算深度值(我在examples/js/shaders/SSAOShader.js
中找到了readDepth()中的相应代码,我将其移植到JS):
function computeDepth() {
const cameraFarPlusNear = cameraFar + cameraNear;
const cameraFarMinusNear = cameraFar - cameraNear;
const cameraCoef = 2.0 * cameraNear;
let z = unpackRGBAToDepth(rgbaBuffer);
return cameraCoef / (cameraFarPlusNear - z * cameraFarMinusNear);
}
现在,因为这个函数返回范围0..1中的值,我认为它是剪辑空间坐标中的深度,所以我使用以下方法将它们转换为“真实”单位:
const depth = camera.near + depth * (camera.far - camera.near);
这些计算显然略显偏差,我没有弄清楚有关深度存储的数学和细节。 有人可以指出我犯的错误吗?
首先,我认为应该可以在我的unproject-call中使用解压缩的深度值作为z的值,如下所示:
const x = mouseX/w * 2 - 1;
const y = -mouseY/h * 2 + 1;
const v = new THREE.Vector3(x, y, depth).unproject(camera);
但是,这也不能使坐标正确。
[编辑1 2017-05-23 11:00CEST]
根据@WestLangleys的评论,我发现了perspectiveDepthToViewZ()
函数听起来应该有所帮助。 写在JS中的功能是
function perspectiveDepthToViewZ(invClipZ, near, far) {
return (near * far) / ((far - near) * invClipZ - far);
}
但是,当使用深度图中的解压缩值调用时,结果会减少几个数量级。 看到这里 。
好的,所以。 终于解决了。 因此,对于遇到类似问题的每个人来说,这是解决方案:
computeDepth函数的最后一行是错误的。 在packing.glsl
有一个函数perspectiveDepthToViewZ
,很容易转换为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);
};
}) ();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.