import torch


def perspective(fovy, aspect, z_near, z_far):
    """perspective (right hand_no)
    Inputs:
    - fovy: float, [batch], fov angle
    - aspect: float, [batch], aspect ratio
    - z_near, z_far: float, [batch], the z-clipping distances
    Returns:
    - proj_mat: float, [batch x 4 x 4]
    """
    tan_half_fovy = torch.tan(fovy / 2.0)
    zeros_pl = torch.zeros_like(fovy)
    ones_pl = torch.ones_like(fovy)

    k1 = -(z_far + z_near) / (z_far - z_near)
    k2 = -2.0 * z_far * z_near / (z_far - z_near)
    return torch.stack(
        [
            1.0 / aspect / tan_half_fovy,
            zeros_pl,
            zeros_pl,
            zeros_pl,
            zeros_pl,
            1.0 / tan_half_fovy,
            zeros_pl,
            zeros_pl,
            zeros_pl,
            zeros_pl,
            k1,
            k2,
            zeros_pl,
            zeros_pl,
            -ones_pl,
            zeros_pl,
        ],
        -1,
    ).view(-1, 4, 4)