【问题标题】:Three.js: split camera frustum (like in Cascade Shadow Mapping)Three.js:分割相机平截头体(如级联阴影映射)
【发布时间】:2019-12-31 22:56:12
【问题描述】:

有一种称为级联阴影映射的技术(我发现有用的教程:link),我正在尝试在 Three.js 中做类似的事情,除了我的情况更容易,因为“光源”是相机所在的位置。

CSM 建议将相机平截头体分成几个部分,并为每个部分创建一个单独的正交投影矩阵。我一直在为此苦苦挣扎。据我了解,首先我需要找到平截头体,所以我尝试了这个:

function cameraToWorld(point, camera) {
    camera.updateWorldMatrix();
    return point.applyMatrix4(camera.matrixWorld);
}

function calculateCameraFrustumCorners(camera) {
    // this camera is an instance of PerspectiveCamera
    const hFOV = 2 * Math.atan(Math.tan(THREE.Math.degToRad(camera.fov) / 2) * camera.aspect);
    const xNear = Math.tan(hFOV / 2) * camera.near;
    const xFar = Math.tan(hFOV / 2) * camera.far;

    const yNear = Math.tan(THREE.Math.degToRad(camera.fov) / 2) * camera.near;
    const yFar = Math.tan(THREE.Math.degToRad(camera.fov) / 2) * camera.far;

    let arr = [new THREE.Vector3(xNear,      yNear,      camera.near),
               new THREE.Vector3(xNear * -1, yNear,      camera.near),
               new THREE.Vector3(xNear,      yNear * -1, camera.near),
               new THREE.Vector3(xNear * -1, yNear * -1, camera.near),
               new THREE.Vector3(xFar,       yFar,       camera.far),
               new THREE.Vector3(xFar  * -1, yFar,       camera.far),
               new THREE.Vector3(xFar,       yFar  * -1, camera.far),
               new THREE.Vector3(xFar  * -1, yFar  * -1, camera.far)];

    return arr.map(function (val) {
        return cameraToWorld(val, camera);
    });
}

然后我在截锥角周围创建一个边界框并使用它来创建一个OrthographicCamera

function getOrthographicCameraForPerspectiveCamera(camera) {
    const frustumCorners = calculateCameraFrustumCorners(camera);

    let minX = Number.MAX_VALUE;
    let maxX = Number.MIN_VALUE;
    let minY = Number.MAX_VALUE;
    let maxY = Number.MIN_VALUE;
    let minZ = Number.MAX_VALUE;
    let maxZ = Number.MIN_VALUE;
    for (let i = 0; i < frustumCorners.length; i++) {
        let corner = frustumCorners[i];
        let vec = new THREE.Vector4(corner.x, corner.y, corner.z, 1);

        minX = Math.min(vec.x, minX);
        maxX = Math.max(vec.x, maxX);
        minY = Math.min(vec.y, minY);
        maxY = Math.max(vec.y, maxY);
        minZ = Math.min(vec.z, minZ);
        maxZ = Math.max(vec.z, maxZ);
    }

    return new THREE.OrthographicCamera(minX, maxX, minY, maxY, minZ, maxZ);
}

然后我将正交相机矩阵传递给着色器并使用它来映射纹理(“阴影贴图”纹理,我使用从上述函数中获得的正交相机进行渲染)。

但这不起作用:我得到的边界框坐标并不能真正为正交相机提供好的参数,它通常接受width / -2, width / 2, height / 2, height / -2, 0.1, 1000 形式的东西,这根本不是我得到的这段代码。我需要对边界框角应用另一个变换吗?比如,在屏幕上而不是在世界空间中获取他们的坐标?我仍然对所有正在使用的坐标系不够熟悉。还是我计算的截锥角有误?

【问题讨论】:

  • Orthographic projection 的观看量是一个长方体。在Perspective projection,观看量将是Frustum(截断的金字塔)
  • @Rabbid76 很抱歉花了这么长时间阅读您的回答!就是现在;我只需要在getOrthographicCameraForPerspectiveCamera 中返回return new THREE.OrthographicCamera(minX / 8, maxX / 8, maxY / 8, minY / 8, minZ, maxZ);,而且我认为平截头体角的计算不正确;但是,您提供的解决方案 (unproject) 更优雅,还提供了解释,所以我接受了。

标签: graphics three.js


【解决方案1】:

Orthographic projection 的查看体积是一个长方体。在Perspective projection,观看量将是Frustum(截断的金字塔)。

与投影类型无关,在标准化设备空间中,查看体积是一个立方体,其中左、下、近角为 (-1, -1, -1),右、上、远角为 (1 ,1, 1)。


如果你想知道查看体的角点,那么在规范化的设备空间中定义点并将它们转换为世界空间`。逆投影矩阵 (.projectionMatrixInverse ) 从归一化设备空间转换到视图空间,逆视图矩阵 (.matrixWorld) 从视图空间转换到世界空间:

let ndc_corners = [
    [-1,-1,-1], [1,-1,-1], [-1,1,-1], [1,1,-1],
    [-1,-1, 1], [1,-1, 1], [-1,1, 1], [1,1, 1]];

let view_corners = []
for (let i=0; i < ndc_corners.length; ++i) {
    let ndc_v = new THREE.Vector3(...ndc_corners[i]);
    view_corners.push(ndc_v.applyMatrix4(camera.projectionMatrixInverse));
}

let world_corners = []
for (let i=0; i < view_corners.length; ++i) {
    let view_v = view_corners[i].clone();
    world_corners.push(view_v.applyMatrix4(camera.matrixWorld));
}

在三个可以大大简化的地方,使用Vector3.unproject( camera : Camera)

let ndc_corners = [
    [-1,-1,-1], [1,-1,-1], [-1,1,-1], [1,1,-1],
    [-1,-1, 1], [1,-1, 1], [-1,1, 1], [1,1, 1]];

let world_corners = []
for (let i=0; i < ndc_corners.length; ++i) {
    let ndc_v = new THREE.Vector3(...ndc_corners[i]);
    world_corners.push(ndc_v.unproject(camera));
}

【讨论】:

    猜你喜欢
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 2014-10-11
    • 2016-06-21
    相关资源
    最近更新 更多