Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    float y_pov = 45.0f;
    float near_plane = 0.01f;
    float far_plane = 100.0f;
    vec3 eye(0, 0, -1);
    vec3 look_at(0, 0, 0);
    vec3 up(0, 1, 0);
    vec3 world_pos(3, 1, -10);
    int viewport_width = 800;
    int viewport_height = 600;

    mat4 projection_mat = perspective(
        glm::radians(y_pov),
        float(viewport_width) / float(viewport_height),
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        eye,
        look_at,
        up
    );

    mat4x4 mvp = projection_mat * view_mat;

    vec2 screen_pos = world_to_screen(world_pos, mvp, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;


 // Thanks to JoeJ from gamedev.net for this code
vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    float y_pov = 45.0f;
    float near_plane = 0.01f;
    float far_plane = 100.0f;
    vec3 eye(0, 0, -1);
    vec3 look_at(0, 0, 0);
    vec3 up(0, 1, 0);
    vec3 world_pos(3, 1, -10);
    int viewport_width = 800;
    int viewport_height = 600;

    mat4 projection_mat = perspective(
        glm::radians(y_pov),
        float(viewport_width) / float(viewport_height),
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        eye,
        look_at,
        up
    );

    mat4x4 mvp = projection_mat * view_mat;

    vec2 screen_pos = world_to_screen(world_pos, mvp, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;


// Thanks to JoeJ from gamedev.net for this code
gamedev.net
vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    float y_pov = 45.0f;
    float near_plane = 0.01f;
    float far_plane = 100.0f;
    vec3 eye(0, 0, -1);
    vec3 look_at(0, 0, 0);
    vec3 up(0, 1, 0);
    vec3 world_pos(3, 1, -10);
    int viewport_width = 800;
    int viewport_height = 600;

    mat4 projection_mat = perspective(
        glm::radians(y_pov),
        float(viewport_width) / float(viewport_height),
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        eye,
        look_at,
        up
    );

    mat4x4 mvp = projection_mat * view_mat;

    vec2 screen_pos = world_to_screen(world_pos, mvp, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;


// Thanks to JoeJ from gamedev.net
vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    float y_pov y_fov = 45.0f;
    float near_plane = 0.01f;
    float far_plane = 100.0f;
    vec3 eye(0, 0, -1);
    vec3 look_at(0, 0, 0);
    vec3 up(0, 1, 0);
    vec3 world_pos(3, 1, -10);
    int viewport_width = 800;
    int viewport_height = 600;

    mat4 projection_mat = perspective(
        glm::radians(y_pov),
glm::radians(y_fov),
        float(viewport_width) / float(viewport_height),
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        eye,
        look_at,
        up
    );

    mat4x4 mvp = projection_mat * view_mat;

    vec2 screen_pos = world_to_screen(world_pos, mvp, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;


// Thanks to JoeJ from gamedev.net
 vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_fov y_fov_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 eye(0, camera_pos(0, 0, -1);
    const vec3 look_at(0, look_at_pos(0, 0, 0);
    const vec3 up(0, up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        glm::radians(y_fov),
        float(viewport_width) / float(viewport_height),
radians(y_fov_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        eye,
        look_at,
        up
camera_pos,
        look_at_pos,
        up_vector
    );

    mat4x4 mvp 
    mat4 mvp_mat = projection_mat * view_mat;

view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_fov_degrees y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_fov_degrees),
radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the matrix. I believe that this is analogous to focal length.

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection and view matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

void get_look_at_matrix(float eyex, float eyey, float eyez,
                        float centrex, float centrey, float centrez,
                        float upx, float upy, float upz,
                        float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
    vertex_3 f, up, s, u;

    f.x = centrex - eyex;
    f.y = centrey - eyey;
    f.z = centrez - eyez;
    f.normalize();

    up.x = upx;
    up.y = upy;
    up.z = upz;
    up.normalize();

    s = f.cross(up);
    s.normalize();

    u = s.cross(f);
    u.normalize();

    mat[0] = s.x;  mat[4] = s.y;  mat[8] = s.z;   mat[12] = 0;
    mat[1] = u.x;  mat[5] = u.y;  mat[9] = u.z;   mat[13] = 0;
    mat[2] = -f.x; mat[6] = -f.y; mat[10] = -f.z; mat[14] = 0;
    mat[3] = 0;    mat[7] = 0;    mat[11] = 0;    mat[15] = 1;

    float translate[16];
    translate[0] = 1; translate[4] = 0; translate[8] = 0;  translate[12] = -eyex;
    translate[1] = 0; translate[5] = 1; translate[9] = 0;  translate[13] = -eyey;
    translate[2] = 0; translate[6] = 0; translate[10] = 1; translate[14] = -eyez;
    translate[3] = 0; translate[7] = 0; translate[11] = 0; translate[15] = 1;

    float temp[16];
    multiply_4x4_matrices(mat, translate, temp);

    for(size_t i = 0; i < 16; i++)
        mat[i] = temp[i];
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];

    vec2 screen;
    screen.x = round(s[0] / s[3] * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] * viewport_height / 2 + viewport_height / 2);

    return screen;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection and view matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

void get_look_at_matrix(float eyex, float eyey, float eyez,
                        float centrex, float centrey, float centrez,
                        float upx, float upy, float upz,
                        float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
    vertex_3 f, up, s, u;

    f.x = centrex - eyex;
    f.y = centrey - eyey;
    f.z = centrez - eyez;
    f.normalize();

    up.x = upx;
    up.y = upy;
    up.z = upz;
    up.normalize();

    s = f.cross(up);
    s.normalize();

    u = s.cross(f);
    u.normalize();

    mat[0] = s.x;  mat[4] = s.y;  mat[8] = s.z;   mat[12] = 0;
    mat[1] = u.x;  mat[5] = u.y;  mat[9] = u.z;   mat[13] = 0;
    mat[2] = -f.x; mat[6] = -f.y; mat[10] = -f.z; mat[14] = 0;
    mat[3] = 0;    mat[7] = 0;    mat[11] = 0;    mat[15] = 1;

    float translate[16];
    translate[0] = 1; translate[4] = 0; translate[8] = 0;  translate[12] = -eyex;
    translate[1] = 0; translate[5] = 1; translate[9] = 0;  translate[13] = -eyey;
    translate[2] = 0; translate[6] = 0; translate[10] = 1; translate[14] = -eyez;
    translate[3] = 0; translate[7] = 0; translate[11] = 0; translate[15] = 1;

    float temp[16];
    multiply_4x4_matrices(mat, translate, temp);

    for(size_t i = 0; i < 16; i++)
        mat[i] = temp[i];
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    float s[4];
    s[0] = (world.x vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * MVP[0][0]) + (world.y * MVP[1][0]) + (world.z * MVP[2][0]) + MVP[3][0];
    s[1] = (world.x * MVP[0][1]) + (world.y * MVP[1][1]) + (world.z * MVP[2][1]) + MVP[3][1];
    s[2] = (world.x * MVP[0][2]) + (world.y * MVP[1][2]) + (world.z * MVP[2][2]) + MVP[3][2];
    s[3] = (world.x * MVP[0][3]) + (world.y * MVP[1][3]) + (world.z * MVP[2][3]) + MVP[3][3];
world4);

    vec2 screen;
    screen.x = round(s[0] / s[3] screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen.y = round(s[1] / s[3] screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen;
screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f);
model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to spit out screen coordinates outside of the view port when the world position is not within the frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to spit out screen coordinates outside of the view port when the world position is not within the associated frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to spit out return screen coordinates that are outside of the view port port, when the world position is not within the associated frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the associated corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from scratch. "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length.length. Regardless, GLM is the way to go!

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go!go! Basically, I implemented my own matrix library, but GLM was so much better.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there's 8 possible regions where it lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for yellow arrows along the edges of the view port:

image description

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there's 8 possible regions where it lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrows arrow along the edges bottom edge of the view port:port):

image descriptionimage description

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there's there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port):port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
    const vec3 up_vector(0, 1, 0);
    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);


    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);
     const vec3 up_vector(0, camera_vector = normalize(camera_pos - look_at_pos);
    const vec3 side_vector = normalize(cross(vec3(0, 1, 0);
0), camera_vector));
    const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

     mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );


    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);

    const vec3 camera_vector = normalize(camera_pos - look_at_pos);
    const  vec3 side_vector;

    if (camera_vector == vec3(0, 1, 0))
        side_vector = normalize(cross(vec3(0, 0, 1), camera_vector));
    else
        side_vector = normalize(cross(vec3(0, 1, 0), camera_vector));
     const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);

    const vec3 camera_vector = normalize(camera_pos - look_at_pos);

    // Be flexible enough to take into account
    // an arbitrary camera_pos and look_at_pos
    vec3 side_vector;

    if (camera_vector == vec3(0, 1, 0))
        side_vector = normalize(cross(vec3(0, 0, 1), camera_vector));
    else
        side_vector = normalize(cross(vec3(0, 1, 0), camera_vector));

    const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    vec4 world4(world.x, world.y, world.z, 1.0f);
    vec4 screen4(MVP * world4);

    if (screen4.w == 0)
        screen4.w = 1;

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 100.0f;
1000.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);

    const vec3 camera_vector = normalize(camera_pos - look_at_pos);

    // Be flexible enough to take into account
    // an arbitrary camera_pos and look_at_pos
    vec3 side_vector;

    if (camera_vector == vec3(0, 1, 0))
        side_vector = normalize(cross(vec3(0, 0, 1), camera_vector));
    else
        side_vector = normalize(cross(vec3(0, 1, 0), camera_vector));

    const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    const vec4 world4(world.x, world.y, world.z, 1.0f);
1);
    vec4 screen4(MVP * world4);

    // Take care of possible division by zero
    if (screen4.w == 0)
        screen4.w = 1;

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 1000.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);

    const vec3 camera_vector = normalize(camera_pos - look_at_pos);

    // Be flexible enough to take into account
    // an arbitrary camera_pos and look_at_pos
    vec3 side_vector;

    if (camera_vector == vec3(0, 1, 0))
        side_vector = normalize(cross(vec3(0, 0, 1), camera_vector));
    else
        side_vector = normalize(cross(vec3(0, 1, 0), camera_vector));

    const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)

Here is code to do it from "scratch". GLM is a header-only library available from https://glm.g-truc.net

Just be mentally prepared for the code to return screen coordinates that are outside of the view port, when the world position is not within the corresponding frustum.

#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <iostream>
using namespace std;



vec2 world_to_screen(const vec3 world, const mat4& MVP, const int viewport_width, const int viewport_height)
{
    const vec4 world4(world.x, world.y, world.z, 1);
    vec4 screen4(MVP * world4);

    // Take care of possible division by zero
    if (screen4.w == 0)
        screen4.w = 1;

    vec2 screen2;
    screen2.x = round(screen4.x / screen4.w * viewport_width / 2 + viewport_width / 2);
    screen2.y = round(screen4.y / screen4.w * viewport_height / 2 + viewport_height / 2);

    return screen2;
}



int main(void)
{
    const float y_field_of_view_degrees = 45.0f;
    const float near_plane = 0.01f;
    const float far_plane = 1000.0f;
    const vec3 camera_pos(0, 0, -1);
    const vec3 look_at_pos(0, 0, 0);

    const vec3 camera_vector = normalize(camera_pos - look_at_pos);

    // Be flexible enough to take into account
    // an arbitrary camera_pos and look_at_pos
    vec3 side_vector;

    if (camera_vector == vec3(0, 1, 0))
        side_vector = normalize(cross(vec3(0, 0, 1), camera_vector));
    else
        side_vector = normalize(cross(vec3(0, 1, 0), camera_vector));

    const vec3 up_vector = cross(camera_vector, side_vector);

    const vec3 world_pos(3, 1, -10);
    const int viewport_width = 800;
    const int viewport_height = 600;
    const float aspect_ratio = static_cast<float>(viewport_width) / static_cast<float>(viewport_height);

    mat4 projection_mat = perspective(
        radians(y_field_of_view_degrees),
        aspect_ratio,
        near_plane,
        far_plane
    );

    mat4 view_mat = lookAt(
        camera_pos,
        look_at_pos,
        up_vector
    );

    mat4 model_mat(1.0f); // Identity matrix

    mat4 mvp_mat = projection_mat * view_mat * model_mat;
    vec2 screen_pos = world_to_screen(world_pos, mvp_mat, viewport_width, viewport_height);

    cout << screen_pos.x << ' ' << screen_pos.y << endl;

    return 0;
}

If you're interested, here is a custom projection matrix code that I wrote before I started to use GLM:

void get_perspective_matrix(float fovy, float aspect, float znear, float zfar, float (&mat)[16])
{
    const float pi = 4.0f*atanf(1.0);

    // Convert fovy to radians, then divide by 2
    float       f = 1.0f / tan(fovy/360.0f*pi);

    mat[0] = f/aspect; mat[4] = 0; mat[8] = 0;                              mat[12] = 0;
    mat[1] = 0;        mat[5] = f; mat[9] = 0;                              mat[13] = 0;
    mat[2] = 0;        mat[6] = 0; mat[10] = (zfar + znear)/(znear - zfar); mat[14] = (2.0f*zfar*znear)/(znear - zfar);
    mat[3] = 0;        mat[7] = 0; mat[11] = -1;                            mat[15] = 0;
}

You can see that f plays a major role in the projection matrix. I believe that this is analogous to focal length. Regardless, GLM is the way to go! Basically, I implemented my own matrix library, but GLM was so much better.

As for screen coordinates lying outside of the view port, there are 8 possible regions where it could lie. Check out this screen shot from a game I'm making, illustrating what I mean (look for the yellow arrow along the bottom edge of the view port, which indicates the presence of an enemy game piece that lies outside of the frustum)):

image description

I place the game pieces on screen using the world to screen coordinate code. Works good.

If you like this answer, please upvote it. :) :)