Understanding the math behind perspective matrix in OpenGL

I've been trying to figure out the math behind perspective matrix for 2 weeks now but I'm failing badly. I understand the theory behind the perspective matrix but I am not sure how the math works.

The code:

def perspective(fov: Float, aspect: Float, zNear: Float, zFar: Float) = {
val h = Math.tan(fov * 0.5f).toFloat
val c00 = 1.0f / (h * aspect)
val c11 = 1.0f / h
val c22 = (zFar + zNear) / (zNear - zFar)
val c23 = (zFar + zFar) * zNear / (zNear - zFar)

Matrix4(
c00,    0,      0,     0,
0,    c11,      0,     0,
0,      0,    c22,   c23,
0,      0,     -1,     0
)
}

What I understand

I've seen video tutorials by Jorge Rodriguez & Arpan Pathak but I cannot fully relate it to the perspective matrix in the function above.

• Congruent triangles: Following Arpan's video I understand that to project a point P=(x, y, z) onto the a 2D XY plane I need to create a frustum and which is then used to find it's projection P'=(x', y', z') using congruent triangles relationship. Arpan's video and the final matrix makes sense but I do not see how it relates to the perspective function above? Here is my attempt.

• • Following the above I switched the frustum in opposite direction to visualise how it would apply in OpenGL using the perspective function above with zFar and zNear parameters but it's no way close to the perspective matrix in the code.

• • I worked it out from this link. songho.ca/opengl/gl_projectionmatrix.html Jun 23 '21 at 13:33
• I'm unable to follow. I feel like it lacks explanation on the math. Jun 24 '21 at 10:33