
Pendahuluan
Ketika suatu titik, garis atau poligon kompleks digambar dalam dunia tiga dimensi, hasil akhirnya pada akhirnya akan ditampilkan pada layar dua dimensi yang datar. Dengan demikian, objek tiga dimensi melalui jalur transformasi tertentu, berubah menjadi sekumpulan piksel yang ditampilkan dalam jendela dua dimensi.
Pengembangan perangkat lunak yang menerapkan grafik tiga dimensi telah datang, terlepas dari mana yang Anda pilih, untuk kira-kira konsep yang sama dari deskripsi matematika dan algoritmik dari transformasi di atas. Secara ideologis, API grafik "bersih" seperti OpenGL, dan mesin permainan keren seperti Unity dan Unreal, menggunakan mekanisme serupa untuk menggambarkan transformasi adegan tiga dimensi. OpenSceneGraph tidak terkecuali.
Pada artikel ini, kami akan meninjau mekanisme untuk mengelompokkan dan mengubah objek tiga dimensi di OSG.
1. Matriks model, matriks tampilan dan matriks proyeksi
Tiga matriks dasar yang terlibat dalam transformasi koordinat terlibat dalam transformasi antara sistem koordinat yang berbeda. Seringkali, dalam istilah OpenGL, mereka disebut
matriks model , matriks tampilan, dan
matriks proyeksi .
Matriks model digunakan untuk menggambarkan lokasi objek di dunia 3D. Ini mengubah simpul dari
sistem koordinat lokal objek ke
sistem koordinat dunia . Omong-omong, semua sistem koordinat dalam OSG tidak
kidal .
Langkah selanjutnya adalah transformasi koordinat dunia menjadi ruang tampilan, dilakukan dengan menggunakan matriks tampilan. Misalkan kita memiliki kamera yang terletak di asal sistem koordinat dunia. Matriks terbalik dengan matriks transformasi kamera sebenarnya digunakan sebagai matriks tampilan. Dalam sistem koordinat tangan kanan, OpenGL, secara default, selalu mendefinisikan kamera yang terletak pada titik (0, 0, 0) dari sistem koordinat global dan diarahkan sepanjang arah negatif dari sumbu Z.
Saya perhatikan bahwa dalam OpenGL model matrix dan view matrix tidak dipisahkan. Namun, model-view matrix ditentukan di sana, yang mengubah koordinat lokal objek menjadi koordinat ruang tampilan. Matriks ini, pada kenyataannya, adalah produk dari matriks model dan matriks bentuk. Dengan demikian, transformasi vertex V dari koordinat lokal ke ruang bentuk dapat ditulis secara kondisional sebagai produk
Ve = V * modelViewMatrix
Tugas penting berikutnya adalah menentukan bagaimana objek 3D akan diproyeksikan ke bidang layar dan menghitung apa yang disebut
kliping piramida - area ruang yang berisi objek yang akan ditampilkan di layar. Matriks proyeksi digunakan untuk menentukan piramida kliping yang didefinisikan di ruang dunia oleh enam pesawat: kiri, kanan, bawah, atas, dekat dan jauh. OpenGL menyediakan fungsi gluPerapective (), yang memungkinkan Anda untuk menentukan piramida kliping dan cara memproyeksikan dunia tiga dimensi ke pesawat.
Sistem koordinat yang diperoleh setelah transformasi di atas disebut
sistem koordinat dinormalisasi perangkat , memiliki rentang koordinat -1 hingga 1 pada setiap sumbu dan tidak kidal. Dan, sebagai langkah terakhir, data yang diterima diproyeksikan ke port tampilan (viewport) dari jendela, ditentukan oleh persegi panjang area klien dari jendela. Setelah itu, dunia 3D muncul di layar 2D kami. Nilai akhir dari koordinat layar dari simpul Vs dapat dinyatakan dengan transformasi berikut
Vs = V * modelViewMatrix * projectionMatrix * windowMatrix
atau
Vs = V * MVPW
di mana MVPW adalah matriks transformasi yang setara dengan produk dari tiga matriks: matriks model-view, matriks proyeksi, dan matriks jendela.
Vs dalam situasi ini adalah vektor tiga dimensi yang menentukan posisi piksel 2D dengan nilai kedalaman. Melawan operasi transformasi koordinat, kami mendapatkan garis dalam ruang tiga dimensi. Oleh karena itu, titik 2D dapat dianggap sebagai dua titik - satu di dekat (Zs = 0), yang lain di bidang kliping jauh (Zs = 1). Koordinat titik-titik ini dalam ruang tiga dimensi
V0 = (Xs, Ys, 0) * invMVPW V1 = (Xs, Ys, 1) * invMVPW
di mana invMVPW adalah kebalikan dari MVPW.
Dalam semua contoh yang dibahas sejauh ini, kami menciptakan objek tiga dimensi tunggal dalam adegan. Dalam contoh ini, koordinat lokal objek selalu bertepatan dengan koordinat global global. Sekarang saatnya berbicara tentang alat yang memungkinkan Anda untuk menempatkan banyak objek di tempat kejadian dan mengubah posisi mereka di ruang angkasa.
2. Node grup
Osg :: Grup kelas adalah yang disebut
simpul grup dari grafik adegan di OSG. Ia dapat memiliki sejumlah node anak, termasuk node daun geometri atau node grup lainnya. Ini adalah node yang paling umum digunakan dengan fungsi yang luas.
Kelas osg :: Grup berasal dari kelas osg :: Node, dan karenanya mewarisi dari kelas osg :: Referenced. osg :: Grup berisi daftar simpul anak, di mana setiap simpul anak dikontrol oleh penunjuk pintar. Ini memastikan bahwa tidak ada kebocoran memori ketika mengalirkan cabang pohon adegan. Kelas ini menyediakan pengembang dengan sejumlah metode publik.
- addChild () - menambahkan node ke akhir daftar node anak. Di sisi lain, ada metode insertChild (), yang menempatkan node anak pada posisi tertentu dalam daftar, yang ditentukan oleh indeks integer atau pointer ke node, dilewatkan sebagai parameter.
- removeChild () dan removeChildren () - menghapus satu node atau sekelompok node.
- getChild () - mendapatkan pointer ke node dengan indeksnya dalam daftar
- getNumChildren () - mendapatkan jumlah node anak yang dilampirkan ke grup ini.
Manajemen Simpul Induk
Seperti yang telah kita ketahui, kelas osg :: group mengelola grup objek anaknya, di antaranya mungkin ada contoh osg :: Geode yang mengontrol geometri objek adegan. Kedua kelas memiliki antarmuka untuk mengelola node induk.
OSG memungkinkan node adegan memiliki beberapa node induk (kami akan membicarakannya nanti). Sementara itu, kita akan melihat metode yang didefinisikan dalam osg :: Node yang digunakan untuk memanipulasi node induk:
- getParent () - mengembalikan pointer dari tipe osg :: Grup yang berisi daftar node induk.
- getNumParants () - mengembalikan jumlah node induk.
- getParentalNodePath () - mengembalikan semua jalur yang mungkin ke simpul akar tempat kejadian dari simpul saat ini. Ini mengembalikan daftar variabel tipe osg :: NodePath.
osg :: NodePath adalah std :: vektor pointer ke adegan adegan.

Misalnya, untuk adegan yang ditunjukkan pada gambar, kode berikut
osg::NodePath &nodePath = child3->getParentalNodePaths()[0]; for (unsigned int i = 0; i < nodePath.size(); ++i) { osg::Node *node = nodePath[i];
akan mengembalikan node Root, Child1, Child2.
Anda sebaiknya tidak menggunakan mekanisme manajemen memori untuk referensi node induk. Ketika node induk dihapus, semua node anak secara otomatis dihapus, yang dapat menyebabkan aplikasi crash.
3. Menambahkan beberapa model ke pohon adegan
Kami menggambarkan mekanisme untuk menggunakan grup dengan contoh berikut.
Contoh grup lengkapmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Group> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc, (void) argv; osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile("../data/cow.osg"); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model1.get()); root->addChild(model2.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Pada dasarnya, contoh berbeda dari semua yang sebelumnya di mana kami memuat dua model tiga dimensi, dan untuk menambahkannya ke adegan kami membuat simpul akar kelompok dan menambahkan model model kami ke dalamnya sebagai simpul anak
osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model1.get()); root->addChild(model2.get());

Hasilnya, kami mendapatkan pemandangan yang terdiri dari dua model - pesawat terbang dan sapi cermin yang lucu. Omong-omong, sapi cermin tidak akan menjadi cermin kecuali jika Anda menyalin teksturnya dari OpenSceneGraph-Data / Images / reflect.rgb ke direktori data / Gambar proyek kami.
The osg :: Grup kelas dapat menerima semua jenis node sebagai anak-anak, termasuk node jenisnya. Sebaliknya, kelas osg :: Geode tidak mengandung node anak sama sekali - ini adalah simpul terminal yang berisi geometri objek adegan. Fakta ini nyaman ketika menanyakan apakah simpul adalah simpul dari tipe osg :: Group atau jenis turunan lain dari osg :: Node. Mari kita lihat contoh kecil.
osg::ref_ptr<osg::Group> model = dynamic_cast<osg::Group *>(osgDB::readNodeFile("../data/cessna.osg"));
Nilai yang dikembalikan oleh fungsi osgDB :: readNodeFile () selalu bertipe osg :: Node *, tetapi dapat dikonversikan ke turunan osg :: turunannya *. Jika simpul model Cessna adalah simpul grup, maka konversi akan berhasil, jika tidak, konversi akan mengembalikan NULL.
Anda juga dapat melakukan trik yang sama yang berfungsi pada kebanyakan kompiler
Di tempat-tempat kode yang kritis terhadap kinerja, lebih baik menggunakan metode konversi khusus
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg"); osg::Group* convModel1 = model->asGroup();
4. Node transformasi
Osg :: Node grup tidak dapat membuat transformasi apa pun kecuali kemampuan untuk pergi ke node anak mereka. OSG menyediakan osg :: Transform class untuk pergerakan geometri spasial. Kelas ini merupakan penerus kelas osg :: Group, tetapi juga abstrak - dalam praktiknya, pewarisnya digunakan sebagai gantinya, yang mengimplementasikan berbagai transformasi spasial dari geometri. Ketika melintasi grafik adegan, osg :: Transform node menambahkan transformasinya ke matriks transformasi OpenGL saat ini. Ini sama dengan mengalikan matriks transformasi OpenGL dengan perintah glMultMatrix ()

Contoh grafik adegan ini dapat diterjemahkan ke dalam kode OpenGL berikut
glPushMatrix(); glMultMatrix( matrixOfTransform1 ); renderGeode1(); glPushMatrix(); glMultMatrix( matrixOfTransform2 ); renderGeode2(); glPopMatrix(); glPopMatrix();
Kita dapat mengatakan bahwa posisi Geode1 diatur dalam sistem koordinat Transform1, dan posisi Geode2 diatur dalam sistem koordinat Transform2, diimbangi relatif terhadap Transform1. Pada saat yang sama, posisi dalam koordinat absolut dapat diaktifkan di OSG, yang akan mengarah pada perilaku objek, setara dengan hasil perintah glGlobalMatrix () OpenGL
transformNode->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
Anda dapat beralih kembali ke mode penentuan posisi relatif
transformNode->setReferenceFrame( osg::Transform::RELATIVE_RF );
5. Konsep matriks transformasi koordinat
Tipe osg :: Matrix adalah tipe OSG dasar yang tidak dikendalikan oleh smart pointer. Ini menyediakan antarmuka untuk operasi pada matriks 4x4 yang menggambarkan transformasi koordinat, seperti memindahkan, memutar, menskalakan dan menghitung proyeksi. Matriks dapat ditentukan secara eksplisit.
Kelas osg :: Matrix menyediakan metode publik berikut:
- postMult () dan operator * () - perkalian yang tepat dari matriks saat ini dengan matriks atau vektor dilewatkan sebagai parameter. Metode preMult () melakukan multiplikasi kiri.
- makeTranslate (), makeRotate () dan makeScale () - mengatur ulang matriks saat ini dan membuat matriks 4x4 yang menggambarkan gerakan, rotasi, dan penskalaan. versi statisnya menerjemahkan (), memutar () dan skala () dapat digunakan untuk membuat objek matriks dengan parameter tertentu.
- invert () - menghitung kebalikan dari matriks saat ini. Versi statis dari inverse () mengambil matriks sebagai parameter dan mengembalikan invers matriks baru ke yang diberikan.
OSG memahami matriks sebagai matriks string, dan vektor sebagai string, oleh karena itu, untuk menerapkan transformasi matriks ke vektor, lakukan ini
osg::Matrix mat = …; osg::Vec3 vec = …; osg::Vec3 resultVec = vec * mat;
Urutan operasi matriks mudah dipahami dengan melihat bagaimana matriks dikalikan untuk mendapatkan konversi yang setara
osg::Matrix mat1 = osg::Matrix::scale(sx, sy, sz); osg::Matrix mat2 = osg::Matrix::translate(x, y, z); osg::Matrix resultMat = mat1 * mat2;
Pengembang harus membaca proses transformasi dari kiri ke kanan. Yaitu, dalam fragmen kode yang dijelaskan, vektor pertama-tama berskala, dan kemudian pergerakannya.
osg :: Matrixf mengandung elemen tipe float.
6. Menggunakan kelas osg :: MatrixTransform
Kami menerapkan pengetahuan teoritis yang diperoleh dalam praktik dengan memuat dua model pesawat pada titik yang berbeda di tempat kejadian.
Teks lengkap contoh transformasimain.h #ifndef MAIN_H #define MAIN_H #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-25.0, 0.0, 0.0)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(25.0, 0.0, 0.0)); transform2->addChild(model.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Contohnya sebenarnya cukup sepele. Memuat model pesawat dari file
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg");
Buat simpul transformasi
osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform;
Kami mengatur model untuk memindahkan model sepanjang sumbu X 25 unit ke kiri sebagai matriks transformasi
transform1->setMatrix(osg::Matrix::translate(-25.0, 0.0, 0.0));
Kami menetapkan model kami untuk simpul transformasi sebagai simpul anak
transform1->addChild(model.get());
Kami melakukan hal yang sama dengan transformasi kedua, tetapi sebagai matriks kami mengatur pergerakan ke kanan sebanyak 25 unit
osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(25.0, 0.0, 0.0)); transform2->addChild(model.get());
Kami membuat simpul root dan sebagai node transformasi untuk itu kami menetapkan node transformasi, transform1 dan transform2
osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get());
Buat pemirsa dan kirimkan simpul root ke sana sebagai data adegan
osgViewer::Viewer viewer; viewer.setSceneData(root.get());
Menjalankan program memberikan gambaran seperti itu

Struktur grafik adegan dalam contoh ini adalah sebagai berikut.

Kita tidak boleh bingung oleh fakta bahwa node transformasi (Anak 1.1 dan Anak 1.2) merujuk pada objek anak yang sama dari model pesawat (Anak 2). Ini adalah mekanisme OSG biasa, ketika satu simpul anak dari grafik adegan dapat memiliki beberapa simpul induk. Dengan demikian, kita tidak harus menyimpan dua contoh model dalam ingatan kita untuk mendapatkan dua pesawat identik di tempat kejadian. Mekanisme ini memungkinkan Anda mengalokasikan memori dengan sangat efisien dalam aplikasi. Model tidak akan dihapus dari memori sampai disebut sebagai anak, setidaknya satu simpul.
Dalam aksinya, kelas osg :: MatrixTransform setara dengan perintah OpenGL glMultMatrix () dan glLoadMatrix (), mengimplementasikan semua jenis transformasi spasial, tetapi sulit digunakan karena kebutuhan untuk menghitung matriks transformasi.
Kelas osg :: PositionAttitudeTransform berfungsi seperti fungsi OpenGL glTranslate (), glScale (), glRotate (). Ini menyediakan metode publik untuk mengkonversi node anak:
- setPosition () - memindahkan node ke titik tertentu dalam ruang yang ditentukan oleh parameter osg :: Vec3
- setScale () - skala objek di sepanjang sumbu koordinat. Faktor penskalaan sepanjang sumbu yang sesuai ditetapkan oleh parameter tipe osg :: Vec3
- setAttitude () - mengatur orientasi spasial objek. Sebagai parameter, osg :: angka empat transformasi rotasi digunakan, konstruktor yang memiliki beberapa kelebihan yang memungkinkan Anda untuk menentukan angka empat baik secara langsung (komponen) dan, misalnya, melalui sudut Euler osg :: Quat (xAngle, osg :: X_AXIS, yAngle, osg :: Y_AXIS, zAngle, osg :: Z_AXIS) (sudut diberikan dalam radian!)
7. Beralih node
Pertimbangkan kelas lain - osg :: Switch, yang memungkinkan Anda untuk menampilkan atau melewati rendering dari node adegan, tergantung pada beberapa kondisi logis. Ini adalah turunan dari kelas osg :: Group dan melampirkan beberapa nilai logis untuk masing-masing anak-anaknya. Ini memiliki beberapa metode publik yang berguna:
- AddChild (), sebagai parameter kedua, kelebihan mengambil kunci logis yang menunjukkan apakah akan menampilkan node ini atau tidak.
- setValue () - mengatur kunci visibilitas / tembus pandang. Dibutuhkan indeks simpul anak yang menarik bagi kami dan nilai kunci yang diinginkan. Dengan demikian, getValue () memungkinkan Anda untuk mendapatkan nilai kunci saat ini dengan indeks dari simpul yang menarik bagi kami.
- setNewChildDefaultValue () - menetapkan nilai default untuk kunci visibilitas semua objek baru yang ditambahkan sebagai anak-anak.
Pertimbangkan penerapan kelas ini dengan contoh.
Sakelar contoh lengkapmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Switch> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile("../data/cessnafire.osg"); osg::ref_ptr<osg::Switch> root = new osg::Switch; root->addChild(model1.get(), false); root->addChild(model2.get(), true); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Contohnya sepele - kami memuat dua model: cessna konvensional dan cessna dengan efek mesin terbakar
osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile("../data/cessnafire.osg");
Namun, kami membuat osg :: Beralih sebagai simpul root, yang memungkinkan kami, ketika menambahkan model sebagai simpul anak, untuk mengatur kunci visibilitas untuk masing-masing
osg::ref_ptr<osg::Switch> root = new osg::Switch; root->addChild(model1.get(), false); root->addChild(model2.get(), true);
Yaitu, model1 tidak akan dirender, dan model2 akan, yang akan kita amati dengan menjalankan program

Dengan menukar nilai kunci kita akan melihat gambar yang berlawanan
root->addChild(model1.get(), true); root->addChild(model2.get(), false);

Memiringkan kedua tombol, kita akan melihat dua model sekaligus
root->addChild(model1.get(), true); root->addChild(model2.get(), true);

Anda dapat mengaktifkan visibilitas dan tembus pandang sebuah simpul, anak dari osg :: Switch, saat bepergian menggunakan metode setValue ()
switchNode->setValue(0, false); switchNode->setValue(0, true); switchNode->setValue(1, true); switchNode->setValue(1, false);
Kesimpulan
Dalam tutorial ini, kami melihat semua kelas simpul menengah utama yang digunakan dalam OpenSceeneGraph. Jadi, kami meletakkan batu bata dasar lagi di dasar pengetahuan tentang perangkat mesin grafis yang tidak diragukan lagi menarik ini. Contoh-contoh yang dibahas dalam artikel, seperti sebelumnya,
tersedia di repositori saya di Github .
Dilanjutkan ...