Trang 2 trên tổng số 4 Đầu tiênĐầu tiên 1234 Cuối cùngCuối cùng
Từ 11 tới 20 trên tổng số 39 kết quả

Đề tài: Nền tảng của đồ họa 3D - 3D Math

  1. #11
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Mặc định Nền tảng của đồ họa 3D - 3D Math

    Ta sẽ phân tích tiếp các hàm quan trọng mà mình code trong ZGraphics3D.

    C++ Code:
    1. ZGraphics3D::ZGraphics3D( ZGraphics *pG, int w, int h)
    2. {      
    3.     Matrix_Identity( m_ModelViewMatrix );
    4.     Matrix_Identity( m_ProjectionMatrix );
    5.  
    6.     m_ViewportMatrix[0] = 0;
    7.     m_ViewportMatrix[1] = 0;
    8.     m_ViewportMatrix[2] = (float)w;
    9.     m_ViewportMatrix[3] = (float)h;
    10.  
    11.     // Doi tuong ve ra cua so
    12.     m_pRender = pG;
    13.    
    14.     // Khoi tao vung dem (Chong giat man hinh)
    15.     m_pBuffer = m_pRender->Create_BackBuffer(w,h);
    16.        
    17.     ...
    18. }

    Đầu tiên khi khởi tạo thì ta sẽ cho ma trận chiếuma trận biến đổi thành ma trận đơn vị.

    Tiếp theo là thiết lập khung nhìn và khởi tạo BackBuffer.
    BackBuffer là một kỹ thuật rất cơ bản trong đồ họa để chống hiện tượng nhấp nháy khi vẽ. Mình sẽ không bàn nhiều về vấn đề này.

  2. #12
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Tiếp theo là 3 hàm biến đổi. Mình đã code nó ở phần trước rồi nên chúng ta chỉ việc gọi lại và biến đổi ma trận ModelView.
    C++ Code:
    1. // Quay ma tran
    2. void ZGraphics3D::Rotate(float fDeg, float x, float y, float z)
    3. {
    4.     Matrix_Rotation( m_ModelViewMatrix, fDeg, x,y,z );
    5. }
    6.  
    7. // Di chuyen ma tran
    8. void ZGraphics3D::Translate(float x, float y, float z)
    9. {
    10.     Matrix_Translate( m_ModelViewMatrix, x,y,z );
    11. }
    12.  
    13. // Ty le man tran
    14. void ZGraphics3D::Scale(float x, float y, float z)
    15. {
    16.     Matrix_Scale( m_ModelViewMatrix, x,y,z );
    17. }

  3. #13
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Cuối cùng là function Draw_Polygon.

    Hàm này nhận tham số là một mảng các Vertex :


    - Đầu tiên ta sẽ biến đổi Mảng Vertex ZVertex v[] này bằng cách chuyển Vertex đó về 1 mảng float[4] và nhân cho ma trận biến đổi

    C++ Code:
    1. void ZGraphics3D::Draw_Polygon( ZVertex3D *v, int nCount )
    2. {  
    3.     float Vertex3D[4];
    4.     int i;
    5.  
    6.     float *aResultVertex;  
    7.     aResultVertex = new float[nCount * 4];
    8.    
    9.     float *p = aResultVertex;
    10.    
    11.     // Bien doi cac vertex
    12.     for (i = 0; i < nCount; i++)
    13.     {
    14.         Vertex3D[0] = v[i].x;
    15.         Vertex3D[1] = v[i].y;
    16.         Vertex3D[2] = v[i].z;
    17.         Vertex3D[3] = 1;
    18.        
    19.         // Bien doi (quay, tinh tien,...)
    20.         this->Transform( Vertex3D, p );
    21.  
    22.         p += 4;
    23.     }
    24. ...
    25.  
    26. }
    27. // Ham Transform bien đoi toa đo
    28. void ZGraphics3D::Transform( float *vertexIn, float *vertexOut )
    29. {  
    30.     Matrix_Mul4( m_ModelViewMatrix, vertexIn, vertexOut );
    31. }

    Như vậy mảng Vertex sẽ biến đổi và chuyển thành mảng aResultVertex

    Tiếp theo ta sẽ chiếu kết quả biến đổi này lên thành tọa độ 2D.

    Dĩ nhiên chúng ta chưa bàn tới phép chiếu.

    Ở đây mình chỉ đơn giản là loại bỏ tọa độ Z đi (chỉ lấy x và y thôi). Đây là phép chiếu song song lên mặt phẳng OXY (nên mất đi trục Z)

    C++ Code:
    1.     // Chieu vertex
    2.     p = aResultVertex;
    3.     for (i = 0; i < nCount; i++)
    4.     {
    5.         // Chuyen truc Y huong len
    6.         p[1] = -p[1];
    7.         // Chuyen ve goc toa do
    8.         p[0] += m_ViewportMatrix[0] + m_ViewportMatrix[2]/2;
    9.         p[1] += m_ViewportMatrix[0] + m_ViewportMatrix[3]/2;
    10.  
    11.         p+=4;
    12.     }

    Công việc còn lại là vẽ danh sách các điểm trong mảng aResultVertex lên màn hình.

    Còn đây là hàm Draw_Polygon đầy đủ

    C++ Code:
    1. void ZGraphics3D::Draw_Polygon( ZVertex3D *v, int nCount )
    2. {  
    3.     float Vertex3D[4];
    4.     int i;
    5.  
    6.     float *aResultVertex;  
    7.     aResultVertex = new float[nCount * 4];
    8.    
    9.     float *p = aResultVertex;
    10.    
    11.     // Bien doi cac vertex
    12.     for (i = 0; i < nCount; i++)
    13.     {
    14.         Vertex3D[0] = v[i].x;
    15.         Vertex3D[1] = v[i].y;
    16.         Vertex3D[2] = v[i].z;
    17.         Vertex3D[3] = 1;
    18.        
    19.         // Bien doi (quay, tinh tien,...)
    20.         this->Transform( Vertex3D, p );
    21.  
    22.         p += 4;
    23.     }  
    24.    
    25.     // Chieu vertex
    26.     p = aResultVertex;
    27.     for (i = 0; i < nCount; i++)
    28.     {
    29.         // Chuyen truc Y huong len
    30.         p[1] = -p[1];
    31.         // Chuyen ve goc toa do
    32.         p[0] += m_ViewportMatrix[0] + m_ViewportMatrix[2]/2;
    33.         p[1] += m_ViewportMatrix[0] + m_ViewportMatrix[3]/2;
    34.  
    35.         p+=4;
    36.     }  
    37.  
    38.     // Ve da giac 2D
    39.     p = aResultVertex;
    40.     int x0,y0,x,y;
    41.  
    42.     for (i = 0; i < nCount; i++)
    43.     {
    44.         // Ve duong thang
    45.         if ( i!= 0 )
    46.             m_pBuffer->Draw_Line(x,y, (int) p[0], (int)p[1]);
    47.         else
    48.         {
    49.             // Neu la Point dau tien -> Luu x0, y0
    50.             x0 = (int)p[0];
    51.             y0 = (int)p[1];
    52.         }
    53.  
    54.         // Luu toa do xy
    55.         x = (int)p[0];
    56.         y = (int)p[1];
    57.  
    58.         p+= 4;
    59.     }
    60.    
    61.     // Ve canh cuoi cung
    62.     if (nCount > 1)
    63.         m_pBuffer->Draw_Line( x0,y0, x,y );
    64.  
    65.     // Huy tai nguyen
    66.     delete aResultVertex;
    67. }

    Mình đã code 1 chương trình trên 2 ngôn ngữ C++ và VB.NET .

    Các bạn có thể Download nó về và chạy thử.



    Còn tiếp...
    Attached Files Attached Files
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 18-10-2008 lúc 01:05 PM.

  4. #14
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Vậy là mình đã giới thiệu cho tương đối về ModelView Matrix rồi. Bây giờ sẽ tạm gác nó qua một bên và bàn tới Projection Matrix.

    III. MA TRẬN CHIẾU (PROJECTION)

    Bạn hãy sống lại thế giới thực một chút nhé.

    Giả sử bạn đang ở 1 nơi dường như không có một chút ánh sáng nào lọt vào ?
    Bạn có nhìn thấy được cái gì không ??

    Dĩ nhiên chắc chắn là không rồi.
    Vậy tại sao khi có ánh sáng bạn lại nhìn thấy mọi vật quanh mình.

    Rõ ràng con mắt của chúng ta chính là 1 Engine 3D hoàn hảo nhất, OpenGL hay DX, cũng như ZGraphics3D của mình cũng chỉ dựa trên nguyên tắc của con mắt mà thôi.

    Chúng ta nhìn thấy được mọi thứ là nhờ có các tia sáng chiếu vào võng mạc. Trong ánh sáng thì tần số của nó sẽ quy định ra màu sắc.

    Và đây chính là nguyên tắc để biến đổi 1 từ Object 3D thành 2D.

    Như vậy để thực hiện 1 phép chiếu chúng ta cần :
    - Tia chiếu (vd : tia sáng từ Object tới mắt)
    - Mặt phẳng chiếu (võng mạc).

    Kết quả chiếu sẽ lưu lại trên mặt phẳng chiếu (võng mạc) và giây thần kinh thị giác sẽ giải mã hình ảnh này để đưa lên bộ não.

    Oh. Vậy những gì trước mắt bạn thấy chỉ là “1 Hình ảnh 2D”. Nghe có vẻ lạ nhỉ! Nhưng đó là sự thật vì bạn chỉ thực sự thấy được ảnh trên võng mạc. (Đó là lý do tại sao khi bạn bị cận hay viễn thì mọi thứ bị mờ, lúc đó phép chiếu trong mắt của bạn có vấn đề…).

    Nhưng phép chiếu nó là cái gì?? Bản chất của phép chiếu vẫn là 1 phép biến đổi. Có nghĩa là sẽ chuyển x,y,z thành x’,y’,z’. Và công việc của chúng ta là đi tìm ma trận chiếu này.
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 05-11-2008 lúc 05:12 PM.

  5. #15
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    1. Phép chiếu song song:

    + Phép chiếu này được sử dụng trong các bản vẽ kỹ thuật.
    + Đặc điểm của nó là bảo toàn được tính song song và độ dài của đường thẳng qua phép chiếu.



    Ma trận chiếu song song
    Ở ví dụ trước thì mình đã làm phép chiếu song song để vẽ khối hộp rồi.

    Nó rất đơn giản, bạn xem hình phía dưới nhé:



    Trên hình là một không gian Oxyz.
    Giả sử mặt phẳng chiếu là mặt phẳng (α) z = near
    Như vậy thì đường thẳng P1’ P2’ chính là hình chiếu của P1P2 trên mặt phẳng (α).


    Rõ ràng tọa độ P1 (x,y,z) được chiếu thành P1’ (x,y,near)

    Code:
    x' = x ;
    y’ = y ;
    z’ = near ;
    Ma trận của phép chiếu song song sẽ như sau :
    Code:
    1    0    0     0
    0    1    0     0
    0    0    0     0
    0    0   near   1
    Sẽ có rất nhiều ma trận chiếu song song, tùy thuộc vào cách đặt mặt phẳng chiếu…

    Ma trận của mình có lẽ là ma trận đơn giản nhất, nó khác OpenGL rất nhiều đó nhưng mục đích cuối cũng cũng chỉ là kết quả trên màn hình.

  6. #16
    Ngày gia nhập
    07 2008
    Nơi ở
    /media/Anime
    Bài viết
    2,288

    Mặc định Nền tảng của đồ họa 3D - 3D Math

    Hay quá ! Cố lên bạn ZCoder87 !
    Càng yêu mèo thì mèo càng mập. Mèo càng mập ta lại càng yêu.

  7. #17
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    2. Phép chiếu phối cảnh

    Ở hình trên đường thẳng P1P2 được chiếu thành P1’P2’ qua điểm chiếu Convergence Point.
    Đây là phép chiếu trong không gian thực mà ta nhìn thấy, sử dụng trong đồ họa 3D, Games, ảnh chụp hình, quay phim…

    Ma trận chiếu phối cảnh




    Đặt vị trí mắt là ở gốc tọa độ O.
    Mặt phẳng chiếu (α) cách mắt một khoảng near.
    Vấn đề bây giờ là tìm ảnh của P1 trên mặt phẳng (α) hay xác định P1’
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 22-10-2008 lúc 08:50 AM.

  8. #18
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Sẽ phức tạp hơn đây bởi vì nó mất chữ song song rồi.

    Có khá nhiều cách để tìm P1’.
    Code:
    Mình sẽ viết phương trình đường thẳng P1 O và phương trình mặt phẳng (α).
    Rồi giải hệ để suy ra giao điểm => Rút ra ma trận.
    Tuy nhiên cách đó quá dài và cũng không hay chút nào?

    Vậy thì làm sao đây ???

    Nếu cứ nhất quyết phải giải trên không gian 3D thì chắc chắn sẽ rất phức tạp. Do đó ta sẽ chuyển nó về không gian 2D để giải bài toán này.

    - Chiếu P1 và P1’ xuống mặt phẳng (xOz) như sau:



    Mình hy vọng rằng bạn có thế nhìn hình và hiểu.
    Điểm P1 chiếu thành A
    Điểm P2 chiếu thành A’

    Như vậy ta có tam giác OAB sẽ đồng dạng với tam giác OA’B’ (Vì phép chiếu song song bảo toàn đơn vị, đô dài… Tự chứng mình nhé).

    Và khoảng cách A’B’ chính là x’!

    Ta có:
    Code:
    A’B’ / AB = OB’ / OB     (tính chất đồng dạng của tam giác)
    Thế 
    - A’B’ = x’
    - A B  = x
    - O B’ = near
    - O B  = z
    Ta có:
    x’ / x  = near / z
    
    x’ = x * (near/z)
    Và làm tương tự như vậy (chiếu lên zOy) chắc chắn bạn cũng sẽ có được
    y’ = y * (near/z)

    Haha sướng quá phải hong ?

    Code:
    x’ = x * (near/z)
    y’ = y * (near/z)
    z’ = near
    Nhưng mà đưa về ma trận như thế nào đây ???? « Thằng z nó luôn bám theo near như thế này (near/z) »

    Chắc chắn bạn sẽ không bao giờ đưa về như thế này được:
    Code:
    x’ = k*x + a
    y’ = l*x + b
    z’ = m*z + c
    
    (k,l,m,a,b,c là const)
    Và chúng ta phải dùng tới cột thứ 4 của Ma Trận. Chuyển không gian 4D thành 3D.

    Từ đầu tới giờ mình chưa nhắc tới cột này:
    Code:
                        [  a   e   i   m  ]
    [x   y   z   w]  x  [  b   f   j   n  ]
                        [  c   g   k   p  ]
                        [  d   h   l   q  ]
    
    w = m*x + n*y + z*p + q
    Tất cả các ma trận mình đã giới thiệu thì giá trị w luôn là 1
    (vì cột cuối cùng đều là 0 0 0 1). Dĩ nhiên đó là một ý đồ.

    Như vậy nếu :
    Ta lấy x/w y/w hay z/w thì giá trị của x,y,z đều được bảo toàn không thay đổi.

    Ta viết lại thế này nhé
    Code:
    x’ = x * (near/z)
    y’ = y * (near/z)
    z’ = near
    
    
    
    Thành 
    x’ = x / (z/near)
    y’ = y / (z/near)
    z’ = near
    Vậy thì w sẽ là z/near
    Code:
    x’ = x / w
    y’ = y / w
    z’ = near
    w  = z * (1/near)
    Hay ma trận sau:

    Code:
    1    0      0        0
    0    1      0        0
    0    0      0     1/near
    0    0  near*near    0

  9. #19
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Tuy nhiên mình Render với giá trị near = 10 thì nó như thế này:



    Sao nó có chút xíu vậy!!!

    Điều đó là hiển nhiên vì ảnh nhìn thấy trên võng mạc của bạn cũng chút xíu vậy đó.

    Vấn đề ở đây lại là khoảng cách near. Nếu near lớn thì hình sẽ lớn và ngược lại.

    Như vậy ta có thể tỷ lệ nó lên 1 khoảng nào đó cho vừa, ví dụ near lần.

    Ma trận lúc này sẽ như sau:

    Code:
    near     0      0        0
      0    near     0        0
      0      0      0     1/near
      0      0  near*near    0
    Giá trị near thông thường từ 10 – 30 là đẹp.

    Trường hợp render lỗi

    Là trường hợp Vertex nằm phía sau mặt phẳng chiếu (z < near ) giống hình.



    - Điều này có nghĩa là chúng ta đang vẽ hình ở phía sau lưng.
    - Để giải quyết vấn đề này ta phải dùng phương pháp Clip. Cắt bỏ những gì không thể thấy được ở phía sau:
    - Ta sẽ bàn luận nó sau nhé…

  10. #20
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    3. Coding…

    a. Phép chiếu song song :
    Code:
    1    0    0     0
    0    1    0     0
    0    0    0     0
    0    0   near   1
    C++ Code:
    1. void Matrix_Ortho( float *A, float left, float right, float bottom, float top, float near, float far)
    2. {
    3.     float OrthoMatrix[16];
    4.     float Result[16];
    5.  
    6.     Matrix_Identity( OrthoMatrix );
    7.    
    8.     OrthoMatrix[10] = 0;
    9.     OrthoMatrix[11] = near;
    10.  
    11.     Matrix_Mul16(A, OrthoMatrix ,Result);  
    12.     memcpy( (void*) A, (void*) Result, sizeof( Result ) );
    13. }

    a. Phép chiếu phối cảnh :

    Code:
    near     0      0        0
      0    near     0        0
      0      0      0     1/near
      0      0  near*near    0
    C++ Code:
    1. void Matrix_Frustum( float *A, float left, float right, float bottom, float top, float near, float far)
    2. {
    3.     float FrustumMatrix[16];
    4.     float Result[16];
    5.  
    6.     Matrix_Identity(FrustumMatrix);
    7.    
    8.     // Chieu
    9.     FrustumMatrix[14] = 1.0f/near;
    10.     FrustumMatrix[15] = 0;
    11.     FrustumMatrix[11]   = near*near;
    12.  
    13.     // Ty le
    14.     FrustumMatrix[0]    = near;
    15.     FrustumMatrix[5]    = near;
    16.     FrustumMatrix[10]   = 0;
    17.  
    18.     Matrix_Mul16(A, FrustumMatrix ,Result);
    19.     memcpy( (void*) A, (void*) Result, sizeof( Result ) );
    20. }

    2 Hàm này mình làm tương tự OpenGL. Mặc dù ta chỉ sử dụng giá trị near nhưng các giá trị left, right vẫn cứ để đó. Ta sẽ sử dụng chúng để cắt xén ở phần sau.

    c. Draw Polygon :

    C++ Code:
    1. // Thuc hien phep chieu
    2. void ZGraphics3D::Projection( float *vertexIn, float *vertexOut )
    3. {
    4.     Matrix_Mul4( m_ProjectionMatrix, vertexIn, vertexOut );
    5. }
    6.  
    7. // Ve 1 Polygon
    8. void ZGraphics3D::Draw_Polygon( ZVertex3D *v, int nCount )
    9. {  
    10.     float Vertex3D[4];
    11.     int i;
    12.  
    13.     float *aTransformVertex;   
    14.     float *aProjectionVertex;
    15.  
    16.     aTransformVertex  = new float[nCount * 4];
    17.     aProjectionVertex = new float[nCount * 4];
    18.  
    19.     float *p = aTransformVertex;   
    20.     float *q;
    21.  
    22.     // Bien doi cac vertex
    23.     for (i = 0; i < nCount; i++)
    24.     {
    25.         Vertex3D[0] = v[i].x;
    26.         Vertex3D[1] = v[i].y;
    27.         Vertex3D[2] = v[i].z;
    28.         Vertex3D[3] = 1;
    29.        
    30.         // Bien doi (quay, tinh tien,...)
    31.         this->Transform( Vertex3D, p );
    32.  
    33.         p += 4;
    34.     }  
    35.    
    36.     // Chieu vertex
    37.     p = aProjectionVertex;
    38.     q = aTransformVertex;
    39.  
    40.     for (i = 0; i < nCount; i++)
    41.     {
    42.         this->Projection( q, p );
    43.        
    44.         p[0] /= p[3]; //    x/w
    45.         p[1] /= p[3]; //    y/w
    46.         p[2] /= p[3]; //    z/w
    47.  
    48.         // Chuyen ve goc toa do
    49.         p[0] += m_ViewportMatrix[0] + m_ViewportMatrix[2]/2;
    50.         p[1] += m_ViewportMatrix[0] + m_ViewportMatrix[3]/2;
    51.  
    52.         p+=4;
    53.         q+=4;
    54.     }  
    55.    
    56.     // Ve da giac 2D
    57.     p = aProjectionVertex;
    58.     int x0,y0,x,y;
    59.  
    60.     for (i = 0; i < nCount; i++)
    61.     {
    62.         // Ve duong thang
    63.         if ( i!= 0 )
    64.             m_pBuffer->Draw_Line(x,y, (int) p[0], (int)p[1]);
    65.         else
    66.         {
    67.  
    68.             // Neu la Point dau tien -> Luu x0, y0
    69.             x0 = (int)p[0];
    70.             y0 = (int)p[1];
    71.         }
    72.  
    73.         // Luu toa do xy
    74.         x = (int)p[0];
    75.         y = (int)p[1];
    76.  
    77.         p+= 4;
    78.     }
    79.    
    80.     // Ve canh cuoi cung
    81.     if (nCount > 1)
    82.         m_pBuffer->Draw_Line( x0,y0, x,y );
    83.  
    84.     // Huy tai nguyen
    85.     delete aProjectionVertex;
    86.     delete aTransformVertex;
    87. }

    Khi Render thì trục Y lọt ngược xuống phía dưới nhưng mình không đổi trục tọa độ nữa như lần trước nữa.

    Cứ mặc định để như vậy. Tới phần phần Camera mình sẽ xử lý nó.

    Chương trình minh họa phép chiếu phối cảnh:
    Attached Files Attached Files
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 22-10-2008 lúc 07:39 AM.

Các đề tài tương tự

  1. Bài tập C++ Phép tính +-*/ không sử dụng thư viện math.h
    Gửi bởi tazihaza trong diễn đàn Nhập môn lập trình C/C++
    Trả lời: 3
    Bài viết cuối: 29-03-2012, 08:51 PM
  2. Lập trình C Định nghĩa lại các toán tử +, -, x, /...và thư viện math.h
    Gửi bởi nakamurra trong diễn đàn Thắc mắc lập trình C/C++/C++0x
    Trả lời: 4
    Bài viết cuối: 24-03-2012, 12:16 PM
  3. Hỏi về hàm Math.pow
    Gửi bởi Batchuoc_09 trong diễn đàn Nhập môn lập trình C#, ASP.NET
    Trả lời: 6
    Bài viết cuối: 03-08-2009, 09:04 PM
  4. Cho hỏi về hàm Math.Sin() và Math.Asin()
    Gửi bởi BuithiHa trong diễn đàn Thắc mắc lập trình C#
    Trả lời: 2
    Bài viết cuối: 12-07-2007, 10:28 PM
  5. Cho mình hỏi về một chút về math.h
    Gửi bởi nokia5510vn trong diễn đàn Nhập môn lập trình C/C++
    Trả lời: 4
    Bài viết cuối: 13-05-2007, 01:45 PM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn