Mari Membuat Mesin Grafik 3D: Transformasi Linier
Indonesian (Bahasa Indonesia) translation by Septriansyah Rehan (you can also view the original English article)
Selamat datang di bagian kedua dari seri 3D Graphics Engine! Kali ini kita akan berbicara tentang transformasi linear, yang akan memungkinkan kita mengubah properti seperti rotasi dan penskalaan vektor, dan melihat bagaimana menerapkannya ke kelas yang sudah dibangun.
Jika Anda belum membaca bagian pertama dari seri ini, saya sarankan Anda melakukannya sekarang. Kalau-kalau Anda tidak ingat, berikut adalah rekap singkat dari apa yang dibuat terakhir kali:
Point Class { Variables: num tuple[3]; //(x,y,z) Operators: Point AddVectorToPoint(Vector); Point SubtractVectorFromPoint(Vector); SubtractPointFromPoint(Point); Functions: //draw a point at its position tuple with your favorite graphics API drawPoint; } Vector Class { Variables: num tuple[3]; //(x,y,z) Operators: Vector AddVectorToVector(Vector); Vector SubtractVectorFromVector(Vector); }
Kedua kelas tersebut akan menjadi dasar dari seluruh mesin grafis, di mana yang pertama mewakili sebuah titik (lokasi fisik di dalam ruang Anda) dan yang kedua mewakili vektor (ruang/pergerakan antara dua titik).
Untuk diskusi tentang transformasi linear, Anda harus membuat perubahan kecil ke kelas Point: alih-alih mengeluarkan data ke garis konsol seperti sebelumnya, gunakan API Grafik favorit Anda dan minta fungsi menggambar titik saat ini ke layar.
Yayasan Transformasi Linier
Hanya peringatan: Persamaan Transformasi Linier terlihat jauh lebih buruk daripada yang sebenarnya. Akan ada beberapa trigonometri yang terlibat, tetapi Anda tidak harus benar-benar tahu bagaimana melakukan trigonometri: Saya akan menjelaskan apa yang harus Anda berikan pada setiap fungsi dan apa yang akan Anda keluarkan, dan untuk hal-hal di antaranya Anda hanya dapat menggunakan kalkulator atau perpustakaan matematika yang mungkin dimiliki.
Semua transformasi linear mengambil bentuk ini:
\[B = F(A)\]
Ini menyatakan bahwa jika Anda memiliki fungsi transformasi linear \(F()\), dan input Anda adalah vektor \(A\), maka output Anda akan menjadi vektor \(B\).
Masing-masing potongan ini - dua vektor dan fungsinya - dapat direpresentasikan sebagai matriks: vektor \(B\) sebagai matriks 1x3, vektor \(A\) sebagai matriks 1x3 lainnya, dan transformasi linear \(F\) sebagai matriks 3x3 (matriks transformasi).
Ini berarti bahwa, ketika Anda memperluas persamaan, tampilannya seperti ini:
\[
\begin{bmatrix}
b_{0} \\
b_{1} \\
b_{2}
\end{bmatrix}
=
\begin{bmatrix}
f_{00} & f_{01} & f_{02}\\
f_{10} & f_{11} & f_{12}\\
f_{10} & f_{11} & f_{12}\\
\end{bmatrix}
\begin{bmatrix}
a_{0}\\
a_{1}\\
a_{2}
\end{bmatrix}
\]
Jika Anda pernah mengikuti kelas trigonometri atau aljabar linier, Anda mungkin mulai mengingat mimpi buruk yang merupakan matematika matriks. Untungnya, ada cara yang lebih sederhana untuk menuliskan persamaan ini untuk menghilangkan sebagian besar masalah dari persamaan tersebut. Ini terlihat seperti ini:
\[
\begin{bmatrix}
b_{0}\\
b_{1}\\
b_{2}
\end{bmatrix}
=
\begin{bmatrix}
f_{00}a_{0} + f_{01}a_{1} + f_{02}a_{2}\\
f_{10}a_{0} + f_{11}a_{1} + f_{12}a_{2}\\
f_{20}a_{0} + f_{21}a_{1} + f_{22}a_{2}\\
\end{bmatrix}
\]
Namun, persamaan ini dapat diubah dengan memiliki input kedua, seperti dalam kasus rotasi, di mana vektor dan jumlah rotasinya harus diberikan. Mari kita lihat bagaimana rotasi bekerja.
Rotasi
Rotasi, menurut definisi, adalah gerakan melingkar dari objek di sekitar titik rotasi. Titik rotasi untuk ruang kita dapat menjadi salah satu dari tiga kemungkinan: baik bidang XY, bidang XZ, atau bidang YZ (di mana setiap bidang terdiri dari dua vektor basis yang dibahas di bagian pertama seri ini. ).



Tiga titik rotasi berarti bahwa kita memiliki tiga matriks rotasi terpisah, sebagai berikut:
Matriks rotasi XY:
\[
\begin{bmatrix}
cos \theta & -sin \theta & 0\\
sin \theta & cos \theta & 0\\
0 & 0 & 1\\
\end{bmatrix}
\]
Matriks rotasi XZ:
\[
\begin{bmatrix}
cos \theta & 0 & sin \theta\\
0 & 1 & 0\\
-sin \theta & 0 & cos \theta
\end{bmatrix}
\]
Matriks rotasi YZ:
\[
\begin{bmatrix}
1 & 0 & 0\\
0 & cos \theta & -sin \theta\\
0 & sin \theta & cos \theta
\end{bmatrix}
\]
Jadi untuk memutar titik \(A\) di sekitar bidang XY sebesar 90 derajat (\(\pi/2\) radian - sebagian besar perpustakaan matematika memiliki fungsi untuk mengubah derajat menjadi radian), Anda akan mengikuti langkah-langkah ini:
\[
\begin{aligned}
\begin{bmatrix}
b_{0}\\
b_{1}\\
b_{2}
\end{bmatrix}
& =
\begin{bmatrix}
cos \frac{\pi}{2} & -sin \frac{\pi}{2} & 0\\
sin \frac{\pi}{2} & cos \frac{\pi}{2} & 0\\
0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
a_{0}\\
a_{1}\\
a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
cos \frac{\pi}{2}a_{0} + -sin \frac{\pi}{2}a_{1} + 0a_{2}\\
sin \frac{\pi}{2}a_{0} + cos \frac{\pi}{2}a_{1} + 0a_{2}\\
0a_{0} + 0a_{1} + 1a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
0a_{0} + -1a_{1} + 0a_{2}\\
1a_{0} + 0a_{1} + 0a_{2}\\
0a_{0} + 0a_{1} + 1a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
-a_{1}\\
a_{0}\\
a_{2}
\end{bmatrix}
\end{aligned}
\]
Jadi jika titik awal Anda \(A\) adalah \((3,4,5)\), maka titik output Anda \(B\) akan menjadi \((-4,3,5)\).
Latihan: Fungsi Rotasi
Sebagai latihan, cobalah membuat tiga fungsi baru untuk kelas Vector
. Seseorang harus memutar vektor di sekitar bidang XY, satu di sekitar bidang YZ, dan satu di sekitar bidang XZ. Fungsi Anda harus menerima jumlah derajat yang diinginkan untuk rotasi sebagai input, dan mengembalikan vektor sebagai output.
Alur dasar fungsi Anda harus sebagai berikut:
- Buat vektor output.
- Ubah input derajat menjadi bentuk radian.
- Pecahkan untuk setiap bagian dari vektor keluaran tuple dengan menggunakan persamaan di atas.
- Kembalikan vektor output.
Scaling
Penskalaan adalah transformasi yang memperbesar atau memperkecil objek berdasarkan skala yang ditetapkan.
Melakukan transformasi ini cukup sederhana (setidaknya dibandingkan dengan rotasi). Transformasi penskalaan membutuhkan dua input: vektor input dan tuple penskalaan 3, yang menentukan bagaimana vektor input harus diskalakan sehubungan dengan masing-masing sumbu basis ruang.
Misalnya, dalam skala penskalaan \((s_{0},s_{1},s_{2})\), \(s_{0}\) mewakili penskalaan sepanjang sumbu X, \(s_{1}\) sepanjang sumbu Y, dan \(s_{2}\) di sepanjang sumbu Z.
Matriks transformasi penskalaan adalah sebagai berikut (where \(s_{0}\), \(s_{1}\), and \(s_{2}\) adalah elemen dari skala 3-tuple):
\[
\begin{bmatrix}
s0 & 0 & 0\\
0 & s1 & 0\\
0 & 0 & s2
\end{bmatrix}
\]
Untuk membuat vektor input A \((a_{0}, a_{1}, a_{2})\) dua kali lebih besar di sepanjang sumbu X (yaitu, menggunakan skala 3-tuple \(S = (2, 1, 1)\)), matematika akan terlihat seperti ini:
\[
\begin{aligned}
\begin{bmatrix}
b_{0}\\
b_{1}\\
b_{2}
\end{bmatrix}
& =
\begin{bmatrix}
s0 & 0 & 0\\
0 & s1 & 0\\
0 & 0 & s2
\end{bmatrix}
\begin{bmatrix}
a_{0}\\
a_{1}\\
a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
2 & 0 & 0\\
0 & 1 & 0\\
0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
a_{0}\\
a_{1}\\
a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
2a_{0} + 0a_{1} + 0a_{2}\\
0a_{0} + 1a_{1} + 0a_{2}\\
0a_{0} + 0a_{1} + 1a_{2}
\end{bmatrix}\\
& =
\begin{bmatrix}
2a_{0}\\
a_{1}\\
a_{2}
\end{bmatrix}
\end{aligned}
\]
Jadi jika diberi vektor input \(A = (3,4,0)\), maka vektor output Anda \(B\) akan menjadi \((6,4,0)\).



Latihan: Fungsi Penskalaan
Sebagai latihan lain, tambahkan fungsi baru ke kelas vektor Anda untuk penskalaan. Fungsi baru ini harus mengambil skala 3-tuple dan mengembalikan vektor output.
Alur dasar fungsi Anda harus sebagai berikut:
- Buat vektor output.
- Pecahkan untuk setiap bagian vektor keluaran tuple dengan menggunakan persamaan di atas (yang dapat disederhanakan menjadi
y0 = x0 * s0; y1 = x1*s1; y2 = x2*s2)
. - Kembalikan vektor output.
Ayo Membuat Sesuatu!
Sekarang setelah Anda memiliki transformasi linier, mari buat program kecil cepat untuk memamerkan keterampilan baru Anda. Kita akan membuat program yang menarik sekelompok titik ke layar, dan kemudian memungkinkan kita untuk memodifikasinya secara keseluruhan dengan melakukan transformasi linear pada mereka.
Sebelum memulai, kita juga ingin menambahkan fungsi lain ke kelas Point
. Ini akan disebut setPointToPoint()
, dan hanya akan mengatur posisi titik saat ini ke titik yang dilewatkan ke sana. Ini akan menerima poin sebagai input, dan tidak akan mengembalikan apa pun.
Berikut adalah beberapa spesifikasi cepat untuk program kita:
- Program ini akan menampung 100 poin dalam sebuah array.
- Ketika tombol D ditekan, program akan menghapus layar saat ini dan menggambar ulang poin.
- Ketika tombol A ditekan, program akan menskala semua lokasi poin sebesar 0.5.
- Ketika tombol S ditekan, program akan menskala semua lokasi poin dengan 2.0.
- Ketika tombol R ditekan, program akan memutar semua lokasi poin sebesar 15 derajat pada bidang XY.
- Ketika tombol Escape ditekan, program akan keluar (kecuali jika Anda membuatnya dengan JavaScript atau bahasa berorientasi web lainnya).
Kelas kita saat ini:
Point Class { Variables: num tuple[3]; //(x,y,z) Operators: Point AddVectorToPoint(Vector); Point SubtractVectorFromPoint(Vector); Vector SubtractPointFromPoint(Point); //sets the current point's position to that of the inputted point Null SetPointToPoint(Point); Functions: //draw a point at its position tuple with your favorite graphics API drawPoint; } Vector Class { Variables: num tuple[3]; //(x,y,z) Operators: Vector AddVectorToVector(Vector); Vector SubtractVectorFromVector(Vector); Vector RotateXY(degrees); Vector RotateYZ(degrees); Vector RotateXZ(degrees); Vector Scale(s0,s1,s2); }
Dengan spesifikasi itu, mari lihat seperti apa kode kita:
main{ //setup for your favorite Graphics API here //setup for keyboard input (may not be required) here //create an array of 100 points Point Array pointArray[100]; for (int x = 0; x < pointArray.length; x++) { //Set its location to a random point on the screen pointArray[x].tuple = [random(0,screenWidth), random(0,screenHeight), random(0,desiredDepth)); } //this function clears the screen and then draws all of the points function redrawScreen() { //use your Graphics API's clear screen function ClearTheScreen(); for (int x = 0; x < pointArray.length; x++) { //draw the current point to the screen pointArray[x].drawPoint(); } } // while the escape is not being pressed, carry out the main loop while (esc != pressed) { // perform various actions based on which key is pressed if (key('d') == pressed) { redrawScreen(); } if (key('a') == pressed) { //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) { //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.scale(0.5,0.5,0.5)); } redrawScreen(); } if(key('s') == pressed) { //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) { //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.scale(2.0,2.0,2.0)); } redrawScreen(); } if(key('r') == pressed) { //create the space's origin as a point Point origin = new Point(0,0,0); Vector tempVector; for (int x = 0; x < pointArray.length; x++) { //store the current vector address for the point, and set the point tempVector = pointArray[x].subtractPointFromPoint(origin); //reset the point so that the scaled vector can be added pointArray[x].setPointToPoint(origin); //scale the vector and set the point to its new, scaled location pointArray[x].addVectorToPoint(tempVector.rotateXY(15)); } redrawScreen(); } } }
Sekarang Anda harus memiliki program kecil yang keren untuk memamerkan semua teknik baru Anda! Anda dapat melihat demo sederhana saya di sini.
Kesimpulan
Meskipun kita tentu saja tidak mencakup setiap kemungkinan transformasi linear yang tersedia, mesin mikro mulai terbentuk.
Seperti biasa, ada beberapa hal yang tertinggal dari mesin kami untuk kesederhanaan (yaitu geser dan refleksi di bagian ini). Jika Anda ingin mengetahui lebih lanjut tentang kedua jenis transformasi linier tersebut, Anda dapat mengetahui lebih lanjut tentang hal itu di Wikipedia dan tautan terkaitnya.
Di bagian selanjutnya dari seri ini, kita akan membahas ruang tampilan yang berbeda dan bagaimana memilih objek yang berada di luar tampilan kita.
Jika Anda memerlukan bantuan ekstra, pergilah ke Envato Studio, di mana Anda dapat menemukan banyak layanan Desain & Pemodelan 3D yang fantastis. Penyedia berpengalaman ini dapat membantu Anda dengan berbagai proyek yang berbeda, jadi cukup telusuri penyedia, baca ulasan dan peringkat, dan pilih orang yang tepat untuk membantu Anda.


