I'm trying to reverse-engineer ARCamera's projectionMatrix(for:viewportSize:zNear:zFar:), but I'm getting wrong values for m[2][0]
and m[2][1]
(column-major). Here's what I got so far (I'm using landscapeRight orientation):
// intrinsics:
// 933.9137 0.0 617.7059
// 0.0 933.9137 363.0624
// 0.0 0.0 1.0
//
// viewportSize:
// width: 700.0, height: 300.0
//
// imageSize (obtained from ARKit's camera):
// width: 1280.0, height: 720.0
//
// zNear: 0.007, zFar: 1300.0
//
// Calculated matrix:
// 1.4592402 0.0 0.034834575 0.0
// 0.0 3.4048939 0.034834575 0.0
// 0.0 0.0 -1.0000054 -0.007000038
// 0.0 0.0 -1.0 0.0
//
// Expected matrix from projectionMatrix(for:viewportSize:zNear:zFar:):
// 1.4592401 0.0 0.034053326 0.0
// 0.0 3.4048936 0.012987971 0.0
// 0.0 0.0 -1.0000054 -0.007000038
// 0.0 0.0 -1.0 0.0
func calculateProjectionMatrix(intrinsics: simd_float3x3,
viewportSize: Size,
imageSize: Size,
zNear: Float,
zFar: Float) -> simd_float4x4 {
let aspect = viewportSize.height / viewportSize.width
let right: Float = imageSize.width - intrinsics[2][0]
let left: Float = -intrinsics[2][0]
let top: Float = right * aspect
let bottom: Float = left * aspect
return simd_float4x4(
simd_float4(2 * intrinsics[0][0] / (right - left), 0, 0, 0),
simd_float4(0, 2 * intrinsics[1][1] / (top - bottom), 0, 0),
simd_float4((right + left) / (right - left), (top + bottom) / (top - bottom), -zFar / (zFar - zNear), -1.0),
simd_float4(0, 0, -zFar * zNear / (zFar - zNear), 0)
)
}
It seems that I'm not setting left
, right
, top
, and bottom
correctly. I know I ignored intrinsics[2][1]
, which I probably shouldn't, but even the result that depended only on right
and left
(m[2][0]
) is wrong.
I read some blog posts (e.g., 1, 2) about this (most of them about OpenGL) but I couldn't get the exact matrix given by this method. Do you have any thoughts of what's wrong with my implementation and what should I do to get the same results as ARKit's function?