web-dev-qa-db-fra.com

Comment construire une matrice de projection en perspective (pas d'API)

Je développe un moteur 3D simple (sans aucune utilisation d'API), j'ai réussi à transformer ma scène en monde et en espace de vision mais j'ai du mal à projeter ma scène (depuis l'espace de vue) en utilisant la matrice de projection en perspective (style OpenGL). Je ne suis pas sûr de la valeur fov, proche et lointaine et la scène que je reçois est déformée. J'espère que si quelqu'un peut m'indiquer comment construire et utiliser correctement la matrice de projection en perspective avec des exemples de codes. Merci d'avance pour votre aide.

La construction de la matrice:

double f = 1 / Math.Tan(fovy / 2);
return new double[,] { 

    { f / Aspect, 0, 0, 0 },
    { 0, f, 0, 0 },
    { 0, 0, (Far + Near) / (Near - Far),  (2 * Far * Near) / (Near - Far) }, 
    { 0, 0, -1, 0 } 
};

L'utilisation de la matrice:

foreach (Point P in T.Points)
{     
    .
    .     // Transforming the point to homogen point matrix, to world space, and to view space (works fine)
    .     

    // projecting the point with getProjectionMatrix() specified in the previous code :      

    double[,] matrix = MatrixMultiply( GetProjectionMatrix(Fovy, Width/Height, Near, Far) , viewSpacePointMatrix );

    // translating to Cartesian coordinates (from homogen):

    matrix [0, 0] /= matrix [3, 0];
    matrix [1, 0] /= matrix [3, 0];
    matrix [2, 0] /= matrix [3, 0];
    matrix [3, 0] = 1;
    P = MatrixToPoint(matrix);

    // adjusting to the screen Y axis:

    P.y = this.Height - P.y;

    // Printing...
}
19
reznic

Voici une implémentation typique d'une matrice de projection en perspective. Et voici un bon lien pour tout expliquer OpenGL Projection Matrix

void ComputeFOVProjection( Matrix& result, float fov, float aspect, float nearDist, float farDist, bool leftHanded /* = true */ )
{
    //
    // General form of the Projection Matrix
    //
    // uh = Cot( fov/2 ) == 1/Tan(fov/2)
    // uw / uh = 1/aspect
    // 
    //   uw         0       0       0
    //    0        uh       0       0
    //    0         0      f/(f-n)  1
    //    0         0    -fn/(f-n)  0
    //
    // Make result to be identity first

    // check for bad parameters to avoid divide by zero:
    // if found, assert and return an identity matrix.
    if ( fov <= 0 || aspect == 0 )
    {
        Assert( fov > 0 && aspect != 0 );
        return;
    }

    float frustumDepth = farDist - nearDist;
    float oneOverDepth = 1 / frustumDepth;

    result[1][1] = 1 / tan(0.5f * fov);
    result[0][0] = (leftHanded ? 1 : -1 ) * result[1][1] / aspect;
    result[2][2] = farDist * oneOverDepth;
    result[3][2] = (-farDist * nearDist) * oneOverDepth;
    result[2][3] = 1;
    result[3][3] = 0;
}
27
Wayne Wang

Une autre fonction qui peut être utile.

Celui-ci est basé sur les paramètres gauche/droite/haut/bas/près/loin (utilisé dans OpenGL):

static void test(){
    float projectionMatrix[16];

    // width and height of viewport to display on (screen dimensions in case of fullscreen rendering)
    float ratio = (float)width/height;
    float left = -ratio;
    float right = ratio;
    float bottom = -1.0f;
    float top = 1.0f;
    float near = -1.0f;
    float far = 100.0f;

    frustum(projectionMatrix, 0, left, right, bottom, top, near, far);

}

static void frustum(float *m, int offset,
                     float left, float right, float bottom, float top,
                     float near, float far) {

    float r_width  = 1.0f / (right - left);
    float r_height = 1.0f / (top - bottom);
    float r_depth  = 1.0f / (far - near);
    float x =  2.0f * (r_width);
    float y =  2.0f * (r_height);
    float z =  2.0f * (r_depth);
    float A = (right + left) * r_width;
    float B = (top + bottom) * r_height;
    float C = (far + near) * r_depth;
    m[offset + 0] = x;
    m[offset + 3] = -A;
    m[offset + 5] = y;
    m[offset + 7] = -B;
    m[offset + 10] = -z;
    m[offset + 11] = -C;
    m[offset +  1] = 0.0f;
    m[offset +  2] = 0.0f;
    m[offset +  4] = 0.0f;
    m[offset +  6] = 0.0f;
    m[offset +  8] = 0.0f;
    m[offset +  9] = 0.0f;
    m[offset + 12] = 0.0f;
    m[offset + 13] = 0.0f;
    m[offset + 14] = 0.0f;
    m[offset + 15] = 1.0f;

}
3
vir us