Sanctus2099
Sanctus2099

Reputation: 1723

Frustum and Perspective matrices

I have some code for transformation matrices and I added some functions that deal with Frustum and Perspective.

The thing is that when I call the perspective everything looks as if I have a really big field of view (ok in the middle of the screen while very stretched at the edges). This effect is worse when I increase the near value.

    void Frustum(T left, T right, T bottom, T top, T zNear, T zFar)
    {
        m[0][0]=2.0f*zNear/(right-left);
        m[1][0]=0.0f;
        m[2][0]=(right+left)/(right-left);
        m[3][0]=0.0f;
        m[0][1]=0.0f;
        m[1][1]=2.0f*zNear/(top-bottom);
        m[2][1]=(top+bottom)/(top-bottom);
        m[3][1]=0.0f;
        m[0][2]=0.0f;
        m[1][2]=0.0f;
        m[2][2]=-(zFar+zNear)/(zFar-zNear);
        m[3][2]=-2.0f*zFar*zNear/(zFar-zNear);
        m[0][3]=0.0f;
        m[1][3]=0.0f;
        m[2][3]=-1.0f;
        m[3][3]=0.0f;
    }

    void Perspective(T fovy,T aspectRatio,T zNear,T zFar)
    {
        T xmin,xmax,ymin,ymax;
        ymax= zNear* tan(fovy*Math<T>::PI/360.0);
        ymin= -ymax;
        xmin= ymin*aspectRatio;
        xmax= ymax*aspectRatio;
        Frustum(xmin,xmax,ymin,ymax,zNear,zFar);
    }

T is a float or a double as the class is templated (for the test I only used floats).

The test values I use are fovy="60" znear="1.0" zfar="1000.0" and when I change them to fovy="60" znear="10.0" zfar="1000.0" it get's a lot worse.

Note that the matrices are DirectX style yet I use them in OpenGL and thus I have change the order of multiplication in the shader.

Do you guys see anything wrong with my code?

Thanks

Upvotes: 1

Views: 5510

Answers (3)

Sanctus2099
Sanctus2099

Reputation: 1723

The problem was that when I started using the matrices that were transposed (DirectX style) I figured I must also transpose the frustum math which isn't a good idea.

This is how the Frustum function should look like:

    void Frustum(T left, T right, T bottom, T top, T zNear, T zFar)
    {
        T zDelta = (zFar-zNear);
        T dir = (right-left);
        T height = (top-bottom);
        T zNear2 = 2*zNear;

        m[0][0]=2.0f*zNear/dir;
        m[0][1]=0.0f;
        m[0][2]=(right+left)/dir;
        m[0][3]=0.0f;
        m[1][0]=0.0f;
        m[1][1]=zNear2/height;
        m[1][2]=(top+bottom)/height;
        m[1][3]=0.0f;
        m[2][0]=0.0f;
        m[2][1]=0.0f;
        m[2][2]=-(zFar+zNear)/zDelta;
        m[2][3]=-zNear2*zFar/zDelta;
        m[3][0]=0.0f;
        m[3][1]=0.0f;
        m[3][2]=-1.0f;
        m[3][3]=0.0f;
    }

Upvotes: 2

comingstorm
comingstorm

Reputation: 26107

I think the problem is in Perspective(): when computing ymax, you should not be multiplying by zNear. Mathematically, you're projecting onto a plane at unit distance, not the zNear plane; that value has no business there.

Try just setting ymax= tan(fovy*Math<T>::PI/360.0) instead. Still no guarantee that it'll be correct, but that should be significantly more sane...

Upvotes: 0

huseyin tugrul buyukisik
huseyin tugrul buyukisik

Reputation: 11920

calculate

z_delta=(zfar-znear);//used two times!
direction=(right-left);//used two times!
height=(top-bottom);//used two times!
znear_2=2.0f*znear; //used 3 times!

only once! and then use it anywhere you want

and, division is so expensive that you can pre-calculate divisions and store in an array, you just need to fetch that pre-calculated elements from array in the execution.

for example: if top and bottom ranges are small, then you can represent the calculations in a small array(actually it is mapping of a divison and a divider)

Upvotes: 1

Related Questions