Skip to content

Commit 93a0302

Browse files
committed
add frustumReverseZ
1 parent 3f8c132 commit 93a0302

File tree

2 files changed

+106
-5
lines changed

2 files changed

+106
-5
lines changed

src/mat4-impl.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear
789789
}
790790

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

834-
if (Number.isFinite(zFar)) {
834+
if (zFar === Infinity) {
835+
dst[10] = 0;
836+
dst[14] = zNear;
837+
} else {
835838
const rangeInv = 1 / (zFar - zNear);
836839
dst[10] = zNear * rangeInv;
837840
dst[14] = zFar * zNear * rangeInv;
838-
} else {
839-
dst[10] = 0;
840-
dst[14] = zNear;
841841
}
842842

843843
return dst;
@@ -929,6 +929,57 @@ export function frustum(left: number, right: number, bottom: number, top: number
929929
return dst;
930930
}
931931

932+
/**
933+
* Computes a 4-by-4 reverse-z perspective transformation matrix given the left, right,
934+
* top, bottom, near and far clipping planes. The arguments define a frustum
935+
* extending in the negative z direction. The arguments near and far are the
936+
* distances to the near and far clipping planes. Note that near and far are not
937+
* z coordinates, but rather they are distances along the negative z-axis. The
938+
* matrix generated sends the viewing frustum to the unit box. We assume a unit
939+
* box extending from -1 to 1 in the x and y dimensions and from 1 (-near) to 0 (-far) in the z
940+
* dimension.
941+
* @param left - The x coordinate of the left plane of the box.
942+
* @param right - The x coordinate of the right plane of the box.
943+
* @param bottom - The y coordinate of the bottom plane of the box.
944+
* @param top - The y coordinate of the right plane of the box.
945+
* @param near - The negative z coordinate of the near plane of the box.
946+
* @param far - The negative z coordinate of the far plane of the box.
947+
* @param dst - Output matrix. If not passed a new one is created.
948+
* @returns The perspective projection matrix.
949+
*/
950+
export function frustumReverseZ(left: number, right: number, bottom: number, top: number, near: number, far = Infinity, dst?: Mat4): Mat4 {
951+
dst = dst || new MatType(16);
952+
953+
const dx = (right - left);
954+
const dy = (top - bottom);
955+
956+
dst[ 0] = 2 * near / dx;
957+
dst[ 1] = 0;
958+
dst[ 2] = 0;
959+
dst[ 3] = 0;
960+
dst[ 4] = 0;
961+
dst[ 5] = 2 * near / dy;
962+
dst[ 6] = 0;
963+
dst[ 7] = 0;
964+
dst[ 8] = (left + right) / dx;
965+
dst[ 9] = (top + bottom) / dy;
966+
dst[11] = -1;
967+
dst[12] = 0;
968+
dst[13] = 0;
969+
dst[15] = 0;
970+
971+
if (far === Infinity) {
972+
dst[10] = 0;
973+
dst[14] = near;
974+
} else {
975+
const rangeInv = 1 / (far - near);
976+
dst[10] = near * rangeInv;
977+
dst[14] = far * near * rangeInv;
978+
}
979+
980+
return dst;
981+
}
982+
932983
let xAxis: Vec3;
933984
let yAxis: Vec3;
934985
let zAxis: Vec3;

test/tests/mat4-test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,56 @@ function check(Type) {
659659
assertEqualApproximately(p[2], 1);
660660
});
661661

662+
it('should compute frustumReverseZ', () => {
663+
const left = 2;
664+
const right = 4;
665+
const top = 10;
666+
const bottom = 30;
667+
const near = 15;
668+
const far = 25;
669+
670+
const dx = (right - left);
671+
const dy = (top - bottom);
672+
const dz = (far - near);
673+
674+
const expected = [
675+
2 * near / dx,
676+
0,
677+
0,
678+
0,
679+
0,
680+
2 * near / dy,
681+
0,
682+
0,
683+
(left + right) / dx,
684+
(top + bottom) / dy,
685+
near / dz,
686+
-1,
687+
0,
688+
0,
689+
near * far / dz,
690+
0,
691+
];
692+
testMat4WithAndWithoutDest((dst) => {
693+
return mat4.frustumReverseZ(left, right, bottom, top, near, far, dst);
694+
}, expected);
695+
});
696+
697+
it('should compute correct frustumReverseZ', () => {
698+
const left = -2;
699+
const right = 4;
700+
const top = 10;
701+
const bottom = 30;
702+
const near = 15;
703+
const far = 25;
704+
const m = mat4.frustumReverseZ(left, right, bottom, top, near, far);
705+
shouldBeCloseArray(vec3.transformMat4([left, bottom, -near], m), [-1, -1, 1], 0.000001);
706+
const centerX = (left + right) * 0.5;
707+
const centerY = (top + bottom) * 0.5;
708+
assertEqualApproximately(vec3.transformMat4([centerX, centerY, -near], m)[2], 1);
709+
assertEqualApproximately(vec3.transformMat4([centerX, centerY, -far], m)[2], 0);
710+
});
711+
662712
it('should compute same frustum as perspective', () => {
663713
const lr = 4;
664714
const tb = 2;

0 commit comments

Comments
 (0)