【发布时间】: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) 更优雅,还提供了解释,所以我接受了。