c++openglprojectionglm-mathfrustum

OpenGL draw a rectangle filling window


I'm trying to understand the OpenGL MVP matrices, and as an exercice I'd like to draw a rectangle filling my window, using the matrices. I thought I would easily find a tutorial for that, but all those I found simply seem to put random values in their MVP matrices setup.

Say my rectangle has these coordinates:

GLfloat vertices[] = {
    -1.0f,  1.0f,  0.0f, // Top-left
     1.0f,  1.0f,  0.0f, // Top-right
     1.0f, -1.0f,  0.0f, // Bottom-right
    -1.0f, -1.0f,  0.0f, // Bottom-left
};

Here are my 2 triangles:

GLuint elements[] = {
    0, 1, 2,
    2, 3, 0
};

If I draw the rectangle with identity MVP matrices, it fills the screen as expected. Now I want to use a frustum. Here are its settings:

float m_fov = 45.0f;
float m_width = 3840;
float m_height = 2160;
float m_zNear = 0.1f;
float m_zFar = 100.0f;

From this I can compute the width / height of my window at z-near & z-far:

float zNearHeight = tan(m_fov) * m_zNear * 2;
float zNearWidth = zNearHeight * m_width / m_height;
float zFarHeight = tan(m_fov) * m_zFar * 2;
float zFarWidth = zFarHeight * m_width / m_height;

Now I can create my view & projection matrices:

glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -m_zNear));

I'd now expect this to make my rectangle to fill the window:

glm::mat4 identity = glm::mat4(1.0f);
glm::mat4 rectangleModelMatrix = glm::scale(identity, glm::vec3(zNearWidth, zNearHeight, 1));

But doing so, my rectangle is way too big. What did I miss?


SOLUTION: as @Rabbid76 pointed out, the problem was the computation of my z-near size, which must be:

float m_zNearHeight = tan(glm::radians(m_fov) / 2.0f) * m_zNear * 2.0f;
float m_zNearWidth = m_zNearHeight * m_width / m_height;

Also, I now need to specify my object coordinates in normalized view space ([-0.5, 0.5]) rather than device space ([-1, 1]). Thus my vertices must now be:

GLfloat vertices[] = {
    -0.5f,  0.5f,  0.0f, // Top-left
     0.5f,  0.5f,  0.0f, // Top-right
     0.5f, -0.5f,  0.0f, // Bottom-right
    -0.5f, -0.5f,  0.0f, // Bottom-left
};

Solution

  • The projected height, of an object on a plan which is parallel to the xy plane of the view is

    h' = h * tan(m_fov / 2) / -z
    

    where h is the height of the object on the plane, -z is the depth and m_fov is the field of view angle.

    In your case m_fov is 45° and -z is -0.1 (-m_zNear), thus tan(m_fov / 2) / z is ~4,142.
    Since the height of the quad is 2, the projected height of the quad is ~8,282.

    To create a quad which fits exactly in the viewport, use a filed of view angle of 90° and a distance to the object of 1, because tan(90° / 2) / 1 is 1. e.g:

    float m_fov = 90.0f;
    glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
    glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -1.0f));
    

    If tan(m_fov / 2) == -z, then an object with the bottom of -1 and the top of 1 fits into the viewport.
    Because of the division by z, the projected size of on object on the viewport decrease linear by the distance to the camera.