Skip to content

Commit

Permalink
add frustumReverseZ
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed May 14, 2024
1 parent 3f8c132 commit 93a0302
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
61 changes: 56 additions & 5 deletions src/mat4-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear
}

/**
* Computes a 4-by-4 perspective transformation matrix given the angular height
* Computes a 4-by-4 reverse-z perspective transformation matrix given the angular height
* of the frustum, the aspect ratio, and the near and far clipping planes. The
* arguments define a frustum extending in the negative z direction. The given
* angle is the vertical angle of the frustum, and the horizontal angle is
Expand Down Expand Up @@ -831,13 +831,13 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear
dst[13] = 0;
dst[15] = 0;

if (Number.isFinite(zFar)) {
if (zFar === Infinity) {
dst[10] = 0;
dst[14] = zNear;
} else {
const rangeInv = 1 / (zFar - zNear);
dst[10] = zNear * rangeInv;
dst[14] = zFar * zNear * rangeInv;
} else {
dst[10] = 0;
dst[14] = zNear;
}

return dst;
Expand Down Expand Up @@ -929,6 +929,57 @@ export function frustum(left: number, right: number, bottom: number, top: number
return dst;
}

/**
* Computes a 4-by-4 reverse-z perspective transformation matrix given the left, right,
* top, bottom, near and far clipping planes. The arguments define a frustum
* extending in the negative z direction. The arguments near and far are the
* distances to the near and far clipping planes. Note that near and far are not
* z coordinates, but rather they are distances along the negative z-axis. The
* matrix generated sends the viewing frustum to the unit box. We assume a unit
* box extending from -1 to 1 in the x and y dimensions and from 1 (-near) to 0 (-far) in the z
* dimension.
* @param left - The x coordinate of the left plane of the box.
* @param right - The x coordinate of the right plane of the box.
* @param bottom - The y coordinate of the bottom plane of the box.
* @param top - The y coordinate of the right plane of the box.
* @param near - The negative z coordinate of the near plane of the box.
* @param far - The negative z coordinate of the far plane of the box.
* @param dst - Output matrix. If not passed a new one is created.
* @returns The perspective projection matrix.
*/
export function frustumReverseZ(left: number, right: number, bottom: number, top: number, near: number, far = Infinity, dst?: Mat4): Mat4 {
dst = dst || new MatType(16);

const dx = (right - left);
const dy = (top - bottom);

dst[ 0] = 2 * near / dx;
dst[ 1] = 0;
dst[ 2] = 0;
dst[ 3] = 0;
dst[ 4] = 0;
dst[ 5] = 2 * near / dy;
dst[ 6] = 0;
dst[ 7] = 0;
dst[ 8] = (left + right) / dx;
dst[ 9] = (top + bottom) / dy;
dst[11] = -1;
dst[12] = 0;
dst[13] = 0;
dst[15] = 0;

if (far === Infinity) {
dst[10] = 0;
dst[14] = near;
} else {
const rangeInv = 1 / (far - near);
dst[10] = near * rangeInv;
dst[14] = far * near * rangeInv;
}

return dst;
}

let xAxis: Vec3;
let yAxis: Vec3;
let zAxis: Vec3;
Expand Down
50 changes: 50 additions & 0 deletions test/tests/mat4-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,56 @@ function check(Type) {
assertEqualApproximately(p[2], 1);
});

it('should compute frustumReverseZ', () => {
const left = 2;
const right = 4;
const top = 10;
const bottom = 30;
const near = 15;
const far = 25;

const dx = (right - left);
const dy = (top - bottom);
const dz = (far - near);

const expected = [
2 * near / dx,
0,
0,
0,
0,
2 * near / dy,
0,
0,
(left + right) / dx,
(top + bottom) / dy,
near / dz,
-1,
0,
0,
near * far / dz,
0,
];
testMat4WithAndWithoutDest((dst) => {
return mat4.frustumReverseZ(left, right, bottom, top, near, far, dst);
}, expected);
});

it('should compute correct frustumReverseZ', () => {
const left = -2;
const right = 4;
const top = 10;
const bottom = 30;
const near = 15;
const far = 25;
const m = mat4.frustumReverseZ(left, right, bottom, top, near, far);
shouldBeCloseArray(vec3.transformMat4([left, bottom, -near], m), [-1, -1, 1], 0.000001);
const centerX = (left + right) * 0.5;
const centerY = (top + bottom) * 0.5;
assertEqualApproximately(vec3.transformMat4([centerX, centerY, -near], m)[2], 1);
assertEqualApproximately(vec3.transformMat4([centerX, centerY, -far], m)[2], 0);
});

it('should compute same frustum as perspective', () => {
const lr = 4;
const tb = 2;
Expand Down

0 comments on commit 93a0302

Please sign in to comment.