projection / view matrix: the object is bigger than it should and depth does not affect vertices

Posted by Francesco Noferi on Game Development See other posts from Game Development or by Francesco Noferi
Published on 2014-06-05T06:20:04Z Indexed on 2014/06/05 9:44 UTC
Read the original article Hit count: 346

I'm currently trying to write a C 3D software rendering engine from scratch just for fun and to have an insight on what OpenGL does behind the scene and what 90's programmers had to do on DOS.

I have written my own matrix library and tested it without noticing any issues, but when I tried projecting the vertices of a simple 2x2 cube at 0,0 as seen by a basic camera at 0,0,10, the cube seems to appear way bigger than the application's window. If I scale the vertices' coordinates down by 8 times I can see a proper cube centered on the screen. This cube doesn't seem to be in perspective: wheen seen from the front, the back vertices pe rfectly overlap with the front ones, so I'm quite sure it's not correct.

this is how I create the view and projection matrices (vec4_initd initializes the vectors with w=0, vec4_initw initializes the vectors with w=1):

void mat4_lookatlh(mat4 *m, const vec4 *pos, const vec4 *target, const vec4 *updirection) {
    vec4 fwd, right, up;

    // fwd = norm(pos - target)
    fwd = *target;
    vec4_sub(&fwd, pos);
    vec4_norm(&fwd);

    // right = norm(cross(updirection, fwd))
    vec4_cross(updirection, &fwd, &right);
    vec4_norm(&right);

    // up = cross(right, forward)
    vec4_cross(&fwd, &right, &up);

    // orientation and translation matrices combined
    vec4_initd(&m->a, right.x, up.x, fwd.x);
    vec4_initd(&m->b, right.y, up.y, fwd.y);
    vec4_initd(&m->c, right.z, up.z, fwd.z);
    vec4_initw(&m->d, -vec4_dot(&right, pos), -vec4_dot(&up, pos), -vec4_dot(&fwd, pos));
}

void mat4_perspectivefovrh(mat4 *m, float fovdegrees, float aspectratio, float near, float far) {
    float h = 1.f / tanf(ftoradians(fovdegrees / 2.f));
    float w = h / aspectratio;

    vec4_initd(&m->a, w, 0.f, 0.f);
    vec4_initd(&m->b, 0.f, h, 0.f);
    vec4_initw(&m->c, 0.f, 0.f, -far / (near - far));
    vec4_initd(&m->d, 0.f, 0.f, (near * far) / (near - far));
}

this is how I project my vertices:

void device_project(device *d, const vec4 *coord, const mat4 *transform, int *projx, int *projy) {
    vec4 result;

    mat4_mul(transform, coord, &result);
    *projx = result.x * d->w + d->w / 2;
    *projy = result.y * d->h + d->h / 2;
}

void device_rendervertices(device *d, const camera *camera, const mesh meshes[], int nmeshes, const rgba *color) {
    int i, j;
    mat4 view, projection, world, transform, projview;
    mat4 translation, rotx, roty, rotz, transrotz, transrotzy;
    int projx, projy;

    // vec4_unity = (0.f, 1.f, 0.f, 0.f)
    mat4_lookatlh(&view, &camera->pos, &camera->target, &vec4_unity);
    mat4_perspectivefovrh(&projection, 45.f, (float)d->w / (float)d->h, 0.1f, 1.f);

    for (i = 0; i < nmeshes; i++) {
        // world matrix = translation * rotz * roty * rotx
        mat4_translatev(&translation, meshes[i].pos);
        mat4_rotatex(&rotx, ftoradians(meshes[i].rotx));
        mat4_rotatey(&roty, ftoradians(meshes[i].roty));
        mat4_rotatez(&rotz, ftoradians(meshes[i].rotz));
        mat4_mulm(&translation, &rotz, &transrotz); // transrotz = translation * rotz
        mat4_mulm(&transrotz, &roty, &transrotzy); // transrotzy = transrotz * roty = translation * rotz * roty
        mat4_mulm(&transrotzy, &rotx, &world); // world = transrotzy * rotx = translation * rotz * roty * rotx

        // transform matrix
        mat4_mulm(&projection, &view, &projview); // projview = projection * view
        mat4_mulm(&projview, &world, &transform); // transform = projview * world = projection * view * world

        for (j = 0; j < meshes[i].nvertices; j++) {
            device_project(d, &meshes[i].vertices[j], &transform, &projx, &projy);
            device_putpixel(d, projx, projy, color);
        }
    }
}

this is how the cube and camera are initialized:

// test mesh
cube = &meshlist[0];
mesh_init(cube, "Cube", 8);
cube->rotx = 0.f;
cube->roty = 0.f;
cube->rotz = 0.f;
vec4_initw(&cube->pos, 0.f, 0.f, 0.f);
vec4_initw(&cube->vertices[0], -1.f, 1.f, 1.f);
vec4_initw(&cube->vertices[1], 1.f, 1.f, 1.f);
vec4_initw(&cube->vertices[2], -1.f, -1.f, 1.f);
vec4_initw(&cube->vertices[3], -1.f, -1.f, -1.f);
vec4_initw(&cube->vertices[4], -1.f, 1.f, -1.f);
vec4_initw(&cube->vertices[5], 1.f, 1.f, -1.f);
vec4_initw(&cube->vertices[6], 1.f, -1.f, 1.f);
vec4_initw(&cube->vertices[7], 1.f, -1.f, -1.f);

// main camera
vec4_initw(&maincamera.pos, 0.f, 0.f, 10.f);
maincamera.target = vec4_zerow;

and, just to be sure, this is how I compute matrix multiplications:

void mat4_mul(const mat4 *m, const vec4 *va, vec4 *vb) {
    vb->x = m->a.x * va->x + m->b.x * va->y + m->c.x * va->z + m->d.x * va->w;
    vb->y = m->a.y * va->x + m->b.y * va->y + m->c.y * va->z + m->d.y * va->w;
    vb->z = m->a.z * va->x + m->b.z * va->y + m->c.z * va->z + m->d.z * va->w;
    vb->w = m->a.w * va->x + m->b.w * va->y + m->c.w * va->z + m->d.w * va->w;
}

void mat4_mulm(const mat4 *ma, const mat4 *mb, mat4 *mc) {
     mat4_mul(ma, &mb->a, &mc->a);
     mat4_mul(ma, &mb->b, &mc->b);
     mat4_mul(ma, &mb->c, &mc->c);
     mat4_mul(ma, &mb->d, &mc->d);
}

© Game Development or respective owner

Related posts about matrix

Related posts about projection