Skip to content

Commit

Permalink
Add mat4.perspectiveReverseZ
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman committed May 14, 2024
1 parent db4afec commit 3f8c132
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 4 deletions.
63 changes: 59 additions & 4 deletions src/mat4-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,13 +776,68 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear
dst[13] = 0;
dst[15] = 0;

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

return dst;
}

/**
* Computes a 4-by-4 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
* determined to produce the given aspect ratio. 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 (at -zNear) to 0 (at -zFar) in the z dimension.
*
* @param fieldOfViewYInRadians - The camera angle from top to bottom (in radians).
* @param aspect - The aspect ratio width / height.
* @param zNear - The depth (negative z coordinate)
* of the near clipping plane.
* @param zFar - The depth (negative z coordinate)
* of the far clipping plane. (default = Infinity)
* @param dst - matrix to hold result. If not passed a new one is created.
* @returns The perspective matrix.
*/export function perspectiveReverseZ(fieldOfViewYInRadians: number, aspect: number, zNear: number, zFar = Infinity, dst?: Mat4) {
dst = dst || new MatType(16);

const f = 1 / Math.tan(fieldOfViewYInRadians * 0.5);

dst[ 0] = f / aspect;
dst[ 1] = 0;
dst[ 2] = 0;
dst[ 3] = 0;

dst[ 4] = 0;
dst[ 5] = f;
dst[ 6] = 0;
dst[ 7] = 0;

dst[ 8] = 0;
dst[ 9] = 0;
dst[11] = -1;

dst[12] = 0;
dst[13] = 0;
dst[15] = 0;

if (Number.isFinite(zFar)) {
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
89 changes: 89 additions & 0 deletions test/tests/mat4-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,95 @@ function check(Type) {
shouldBeCloseArray(vec3.transformMat4([0, 0, zFar], m), [0, 0, Infinity], 0.000001);
});

it('should compute perspective reverseZ with zFar', () => {
const fov = 2;
const aspect = 4;
const zNear = 10;
const zFar = 20;
const f = Math.tan(Math.PI * 0.5 - 0.5 * fov);
const rangeInv = 1 / (zFar - zNear);
const expected = [
f / aspect,
0,
0,
0,

0,
f,
0,
0,

0,
0,
zNear * rangeInv,
-1,

0,
0,
zFar * zNear * rangeInv,
0,
];
testMat4WithAndWithoutDest((dst) => {
return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, dst);
}, expected);
});

it('should compute correct perspective reverseZ', () => {
const fov = Math.PI / 4;
const aspect = 2;
const zNear = 10;
const zFar = 20;
const m = mat4.perspectiveReverseZ(fov, aspect, zNear, zFar);
shouldBeCloseArray(vec3.transformMat4([0, 0, -zNear], m), [0, 0, 1], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -15], m), [0, 0, 0.3333333432674408], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -zFar], m), [0, 0, 0], 0.000001);
});

it('should compute perspective reverseZ with zFar at infinity', () => {
const fov = 2;
const aspect = 4;
const zNear = 10;
const zFar = Infinity;
const f = Math.tan(Math.PI * 0.5 - 0.5 * fov);
const expected = [
f / aspect,
0,
0,
0,

0,
f,
0,
0,

0,
0,
0,
-1,

0,
0,
zNear,
0,
];
testMat4WithAndWithoutDest((dst) => {
return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, dst);
}, expected);
});

it('should compute correct perspective reverseZ with zFar at Infinity', () => {
const fov = Math.PI / 4;
const aspect = 2;
const zNear = 10;
const zFar = Infinity;
const m = mat4.perspectiveReverseZ(fov, aspect, zNear, zFar);
shouldBeCloseArray(vec3.transformMat4([0, 0, -zNear], m), [0, 0, 1], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -1000], m), [0, 0, 0.009999999776482582], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -1000000], m), [0, 0, 0.000009999999747378752], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -1000000000], m), [0, 0, 9.99999993922529e-9], 0.000001);
shouldBeCloseArray(vec3.transformMat4([0, 0, -zFar], m), [0, 0, 0], 0.000001);
});

it('should compute ortho', () => {
const left = 2;
const right = 4;
Expand Down

0 comments on commit 3f8c132

Please sign in to comment.