Search code examples
c++openglprojectionglm-math

From perspective to orthographic projections


I'm trying to change my camera projection from perspective to orthographic. At the moment my code is working fine with the perspective projection

m_prespective = glm::perspective(70.0f, (float)DISPLAY_WIDTH / (float)DISPLAY_HEIGHT, 0.01f, 1000.0f);
m_position = glm::vec3(mesh.centre.x, mesh.centre.y, -mesh.radius);
m_forward = centre;
m_up = glm::vec3(0.0f, 1.0f, 0.0f);
return m_prespective * glm::lookAt(m_position, m_forward, m_up);

But as soon as i change it to orthographic projection I can't see my mesh anymore.

m_ortho = glm::ortho(0.0f, (float)DISPLAY_WIDTH, (float)DISPLAY_HEIGHT,5.0f, 0.01f, 1000.0f);
m_position = glm::vec3(mesh.centre.x, mesh.centre.y, -mesh.radius);
m_forward = centre;
m_up = glm::vec3(0.0f, 1.0f, 0.0f);
return m_ortho * glm::lookAt(m_position, m_forward, m_up);

I don't understand what I'm doing wrong.


Solution

  • In perspective projection the term (float)DISPLAY_WIDTH / (float)DISPLAY_HEIGHT is evaluating the picture aspect ratio. This number is going to be close to 1. The left and right clip plane distances at the near plane for perspective projection is aspect * near_distance. More interesting though is the expanse of left-right at the viewing distance, which in your case is abs(m_position.z)= abs(mesh.radius).

    Carrying this over to orthographic projection the left, right, top and bottom clip plane distances should be of the same order of magnitude, so given that aspect is close to 1 the values for left, right, bottom and top should be close to the value of abs(mesh.radius). The resolution of the display in pixels is totally irrelevant except for the aspect ratio.

    Furthermore when using a perspective projection the value for near should be chosen as large as possible so that all desired geometry is visible. Doing otherwise will waste precious depth buffer resolution.

    float const view_distance = mesh.radius + 1;
    
    float const aspect = (float)DISPLAY_WIDTH / (float)DISPLAY_HEIGHT;
    switch( typeof_projection ){
    case perspective:
        m_projection = glm::perspective(70.0f, aspect, 1.f, 1000.0f);
        break;
    
    case ortho:
        m_projection = glm::ortho(
            -aspect * view_distance,
             aspect * view_distance,
            view_distance,
            view_distance,
            -1000, 1000 );
        break;
    }
    
    m_position = glm::vec3(mesh.centre.x, mesh.centre.y, -view_distance);
    m_forward = centre;
    m_up = glm::vec3(0.0f, 1.0f, 0.0f);
    
    return m_projection * glm::lookAt(m_position, m_forward, m_up);