Projection Matrix, Clip Space, Perspective Divide, dan NDC

Untuk mengubah primitif-primitif 3D menjadi pixel-pixel di layar, diperlukan fungsi yang menerima masukan kordinat 3D dan mengembalikan kordinat 2D layar. Diperlukan juga fungsi clipping yang menentukan apakah titik 3D tertentu berada di dalam layar kamera.

Kedua fungsi diatas dapat dicapai dengan menggunakan Projection Matrix. Projection matrix mengubah kordinat 3D yang berada di View Space (kordinat 3D relatif terhadap sistem kordinat kamera) menjadi Clip Space. Pada Clip Space ini, graphics platform akan menentukan apakah suatu titik/vektor berada di dalam layar atau tidak, jika iya, titik/vektor tersebut akan kemudian ditransformasi menjadi kordinat NDC. Tahap transformasi terakhir ini dinamakan Perspective Divide. Vektor hasil Perspective Divide kemudian akan ditransformasikan menjadi kordinat NDC ayng digunakan oleh graphics driver untuk menentukan dimana posisi titik tersebut di layar.

Agar titik kordinat 3D dapat ditransformasikan dengan baik oleh graphics driver, diperlukan Projection Matrix. Untuk menghitung Projection Matrix, perlu diketahui terlebih dahulu tahapan-tahapan transformasi titik yang dilakukan oleh graphics driver dengan dimulai dari tahapan terakhir transformasi yaitu kordinat NDC dan kemudian sampai ke tahapan pertama yaitu Projection Matrix.

Perspective Divide & Kordinat NDC

Tahapan terakhir transformasi adalah transformasi dari titik/vektor Clip Space ke kordinat NDC. Tahapan ini dinamakan Perspective Divide. Terdapat perbedaan konvensi NDC yang digunakan oleh beberapa graphics platform seperti Direct3D dan OpenGL. NDC Direct3D mempunyai range [-1, 1] untuk sumbu X (dari kiri ke kanan) dan Y (dari bawah ke atas), dan range [0, 1] (dari permukaan layar ke masuk dalam layar) untuk sumbu Z. Sedangkan NDC OpenGL mempunyai range [-1, 1] untuk sumbu X (dari kiri ke kanan) dan Y (dari bawah ke atas), dan Z (dari permukaan layar masuk ke dalam layar). Keduanya adalah left-handed coordinate system dengan X mengacu ke kanan, Y mengacu ke atas, dan Z mengacu ke dalam layar.

Output dari Perspective Divide harus berada pada range yang disebutkan diatas untuk dapat digambar oleh graphics driver. Kordinat X dan Y akan digunakan oleh graphics driver untuk menentukan dimana benda itu berada relative terhapan layar komputer, sedangkan kordinat Z digunakan untuk menentukan apakah suatu benda berada di depan atau di belakang benda lainnya.

Untuk mendapatkan kordinat NDC dari titik/vektor Clip Space, V_{NDC} = \frac{V_{clip}}{w_{clip}} dimana V_{clip} adalah hasil dari tahapan sebelumnya.

Pada tahapan ini, juga dilakukan clipping. Clipping dilakukan oleh graphics driver berdasarkan V_{clip}. Hal ini dilakukan karena untuk titik di belakang kamera, dimana z < 0 di View Space, setelah Perspective Divide, titik tersebut akan menjadi z_{NDC} > 0 sehingga yang seharusnya tidak lolos uji clip, malah lolos uji clip.

Pada Direct3D, clipping dilakukan untuk vektor/titik yang tidak memenuhi salah satu dari 3 kondisi ini:

    \begin{alignat*}{1} -w_{clip} \leq x_{clip} \leq w_{clip}\\ -w_{clip} \leq y_{clip} \leq w_{clip}\\ 0 \leq z_{clip} \leq w_{clip}\\ \end{alignat*}

Clipping dilakukan sebelum perspective divide, karena setelah perspective divide jika z_{clip} < 0, maka z_{NDC} > 0. Ini berarti bahwa benda yang tadinya ada di belakang kamera menjadi di depan setelah perspective divide (baca juga di sini). Sehingga, clipping lebih mudah dilakukan sebelum Perspective Divide.

Perspective Projection

Perspective Matrix adalah salah satu matrix projection yang mengubah kordinat View Space menjadi Clip Space.

Y’s FOV for Frustum

Perspective Matrix didefinisikan oleh empat parameter yaitu:

  • Y’s Field of view (fovy) – Sudut segitiga puncak di sisi kanan piramid frustum
  • Aspect ratio – Rasio dari lebar dan tinggi layar
  • Near distance – Jarak near plane dari titik kamera/mata (view/eye point)
  • Far distance – Jarak far plane dari titik kamera/mata (view/eye point)

    \begin{alignat*}{1} \frac{x'}{-n} &= \frac{x}{z}\\ x' &= -\frac{nx}{z}\\ \end{alignat*}

dimana x' adalah kordinat x diproyeksikan ke near plane. Near plane ada di kordinat z = -n karena kordinat View Space adalah right-handed coordinate system untuk keperluan tulisan ini. Kordinat View Space tidak harus left-handed, ini bebas ditentukan oleh penulis program, akan tapi hasil NDC nantinya harus mengikuti konvensi graphics driver yang digunakan. x' kemudian akan ditransformasikan ke kordinat NDC. Karena NDC mempunyai range [-1, +1] sedangkan x' mempunyai range [-w, +w] maka:

    \begin{alignat*}{1} A &= \frac{w}{h}\\ w &= A * h\\ h &= n * tan(\frac{\theta}{2})\\ x_{NDC} &= \frac{x'}{w}\\ x_{NDC} &=  \frac{-nx}{z} * \frac{1}{A*n*tan(\frac{\theta}{2})}\\ x_{NDC} &= -\frac{x}{A*tan(\frac{\theta}{2})} * \frac{1}{z}\\ \end{alignat*}

dengan A adalah Aspect Ratio, w dan h adalah lebar dan panjang layar dan \theta adalah sudut Field Of View.

Kemudian untuk y_{NDC}:

    \begin{alignat*}{1} y_{NDC} &= -\frac{y}{tan(\frac{\theta}{2})} * \frac{1}{z}\\ \end{alignat*}

Dari x_{NDC} dan y_{NDC} dapat dilihat bahwa kedua variabel tersebut tergantung pada variabel z. Sedangkan pada transformasi matrix 3×3 untuk mengubah View Space menjadi NDC, hanya bisa melakukan kalkulasi x_{NDC} = ax + by + cz dengan a, b, dan c adalah elemen pada baris pertama matrix. Karena inilah diperlukan yang namanya perspective divide. Dengan adanya perspective divide, kita dapat mengalikan matrix 4×4 dengan vektor V = <X, Y, Z, 1> yang menghasilkan V_{clip} = <X_{clip}, Y_{clip}, Z_{clip}, Z>. Akan tetapi karena sistem kordinat View Space menggunakan right-handed coordinate system, sedangkan NDC menggunakan left-handed coordinate system, maka elemen Z pada V_{clip} harus dicerminkan menjadi V_{clip} = <X_{clip}, Y_{clip}, Z_{clip}, -Z>.

 

Dengan begitu:

 M_{pers}= \begin{bmatrix}  x_{pers} & 0 & 0 & 0\\ 0 & y_{pers} & 0 & 0\\ 0 & 0 & z_{pers} & B\\ 0 & 0 & C & 0 \end{bmatrix}

Untuk menentukan z_{p}, B, dan C,  perlu diingat bahwa matrix diatas adalah susunan matrix perspective yang akan dikalikan dengan vektor <X, Y, Z, 1>, sehingga:

    \begin{alignat*}{1} M_{pers} *  \begin{bmatrix} X\\ Y\\ Z\\ 1\\ \end{bmatrix} &= \begin{bmatrix} X_{clip}\\ Y_{clip}\\ Z_{clip}\\ -Z\\ \end{bmatrix}\\ \end{alignat*}

Dari persamaan diatas, dapat dicari C:

    \begin{alignat*}{1} C*Z &= -Z\\ C &=-1 \end{alignat*}

dan x_{p}:

    \begin{alignat*}{1} x_{pers} * X &= x_{NDC} -Z\\ x_{pers} &= \frac{1}{A*tan(\frac{\theta}{2})}\\ \end{alignat*}

dan y_{p}:

    \begin{alignat*}{1} y_{pers} * Y &= y_{NDC} * -Z\\ y_{pers} &= \frac{1}{tan(\frac{\theta}{2})}\\ \end{alignat*}

kemudian untuk z_{pers}:

    \begin{alignat*}{1} z_{pers} * Z + B &= z_{NDC} * -Z\\ \end{alignat*}

diketahui dua titik Z = -n harus menghasilkan Z_{NDC} = 0 dan Z = -f harus menghasilkan Z_{NDC} = 1 sehingga:

    \begin{alignat*}{1} z_{pers} * -n + B &= 0\\ B &= z_{pers} * n\\ z_{pers} * -f + B &= f\\ -z_{pers}*f + z_{pers}*n &= f\\ z_{pers}(n-f) &= f\\ z_{pers} &= \frac{f}{n-f}\\ B &= \frac{n*f}{n-f} \end{alignat*}

sehingga

 M_{pers}= \begin{bmatrix}  \frac{1}{A*tan(\frac{\theta}{2})} & 0 & 0 & 0\\ 0 & \frac{1}{tan(\frac{\theta}{2})} & 0 & 0\\ 0 & 0 & \frac{f}{n-f} & \frac{n*f}{n-f}\\ 0 & 0 & -1 & 0 \end{bmatrix}

Referensi:
https://stackoverflow.com/questions/46811240/perspective-division-explanation

https://stackoverflow.com/questions/41952225/why-clipping-should-be-done-in-ccs-not-ndcs/41953552#41953552

https://docs.microsoft.com/en-us/windows/desktop/direct3d9/viewports-and-clipping

Leave a Reply

Your email address will not be published. Required fields are marked *