2013-08-23 107 views
13

Tôi phát triển một công cụ 3D đơn giản (không sử dụng API), biến đổi thành công khung cảnh của tôi thành thế giới và xem không gian nhưng gặp khó khăn khi chiếu cảnh của tôi (từ không gian xem) ma trận chiếu (kiểu OpenGL). Tôi không chắc chắn về fov, gần và xa giá trị và cảnh tôi nhận được là bị bóp méo. Tôi hy vọng nếu ai đó có thể hướng dẫn tôi cách xây dựng và sử dụng ma trận chiếu phối cảnh đúng cách với mã mẫu. Cảm ơn trước sự giúp đỡ nào.Làm thế nào để xây dựng một ma trận chiếu phối cảnh (không có API)

Ma trận xây dựng:

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 } 
}; 

Việc sử dụng ma trận:

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... 
} 
+0

http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix Tôi cũng đề nghị các bài học trước (chiếu điểm và xem 3D). – user18490

Trả lời

23

Sau đây là một implemenation đặc trưng của ma trận chiếu quan điểm. Và đây là một liên kết tốt để giải thích tất cả mọi thứ 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; 
} 
+0

Rất hữu ích, cảm ơn –

+0

Xin lỗi, nhưng uh và uw ở đây là gì? Chiều rộng người dùng và chiều cao của người dùng? – ReX357

+2

@ ReX357 uw = gần/phải, và uh = gần/trên cùng, ở bên phải là tọa độ của đúng kế hoạch clip và trên cùng là tọa độ của mặt phẳng kẹp trên. Khi phép chiếu phối cảnh trên là đối xứng, vì vậy phải = một nửa chiều rộng chân trời và trên = một nửa chiều cao dọc, sau đó uw/uh = trên cùng/bên phải = chiều cao/chiều rộng = 1/khía cạnh –

0

Một chức năng có thể hữu ích.

một này được dựa trên trái/phải/trên/dưới/gần/xa các thông số (được sử dụng trong 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; 

}