2
votes

I'm trying to build my own Rasteriser/Graphics pipeline (mimicking OpenGL) from scratch and I'm having problems with implementing a working perspective projection matrix. This is my current attempt:

template<typename T>
Matrix<T,4,4> Perspective(T fov,T aspect, T near, T far)
{
    T mat00 = 1 / (aspect*tan(0.5f*fov));
    T mat11 = 1 / tan(0.5f*fov);
    T mat22 = (-near-far)/(near-far);
    T mat32 = (2*near*far)/(near-far);
    T data[] = 
    {
        mat00,     0,     0,    0,
        0    , mat11,     0,    0,
        0    ,     0, mat22,mat32,
        0    ,     0,     1,    0
    };
    return Matrix<T,4,4>(data);
}

This is then passed to the vertex processing which performs the following:

    printf("Vertex inital:");vert.Print();
    vert = vert* m_ModelMatrix;

    printf("Vertex model:");vert.Print();
    vert = vert* m_ProjectionMatrix;

    printf("Vertex projection:");vert.Print();

The output of which is (for a randomly selected vertex):

Vertex inital:(1.000000,0.000000,-1.000000,1.000000)
Vertex model:(-1.900000,-2.300000,2.599999,1.000000)
Vertex projection:(-3.669604,-5.552692,2.405004,2.599999)

The above vertex corresponds to one of the vertices in this triangle: enter image description here

My problem is the projection matrix appears to be completely wrong. When I increase the Z translation the triangle moves diagonally towards the top-right corner while appearing smaller, when I decrease the Z value the triangle moves diagonally towards the bottom-left corner while appearing larger. Has anyone else encountered this issue?

For reference, my implementation of the vector*matrix operation:

template<typename T,unsigned int length>
Vector<T,length> Vector<T,length>::operator* (const Matrix<T,length,length> & matrix) const
{
    T temp[length];
    memset(temp,0,length*sizeof(T));
    const T * matrixData = matrix.GetData();
    //For each row in the matrix & in the returned vector...
    for(unsigned int iRow = 0; iRow < length; ++iRow)
    {
        //For each column in this vector & matrix...
        for(unsigned int iColumn = 0; iColumn < length; ++iColumn)
        {
            temp[iRow] += m_Data[iColumn]*matrixData[iRow*length + iColumn];
        }
    }
    return Vector<T,length>(temp);
}
1
Why dont you just use glm instead of writing everything from scratch?Borgleader
That projected (clip-space) vertex is not what you are showing in your screenshot. The coordinate (-3.669604,-5.552692,2.405004,2.599999) is actually outside of your viewport (and thus, part of this triangle should be clipped) -- you can tell this because X and Y are both < -W_clip (-2.599999 in this case). It also sounds like you are forgetting to divide by W, which is what does the perspective scaling.Andon M. Coleman
@Davors72: Right, but if you actually had a working viewport transform, that triangle would be well off to the lower-left hand side. Just looking at the clip-space coordinates I can tell you that much. (-1.41, -2.136, 0.925) is well outside the normalized device coordinate range.Andon M. Coleman
@Davors72: That actually looks right assuming everything is in column-major order. But your projection matrix is row-major. Have you tried transposing your viewport matrix?Andon M. Coleman
I'm not sure what's wrong then. About all I can really say is that if you are trying to mimic OpenGL, there are a few things you are doing that are actually more like D3D than OpenGL: row-major matrices, NDC Z should range from -1 to 1, not 0 to 1 (as your viewport matrix suggests), the traditional OpenGL projection matrix has m23 = -1 (this flips the Z-axis and changes right-handed view-space into left-handed clip-space).Andon M. Coleman

1 Answers

1
votes

So I came back to this project after quite some break and I realised that my projection divide (aka /w) was only dividing the xyz coords. So when I applied the screenspace matrix the translate column was being multiplied by the 'greater than one' W value causing the triangle to move to the upper right as Z decreases.