
Pendahuluan
Berbicara tentang teknik pemrograman khusus untuk OSG
, terakhir kali kami berbicara tentang mekanisme Callback dan implementasinya di mesin. Sudah saatnya untuk melihat kemungkinan yang disediakan mekanisme ini untuk mengelola konten adegan tiga dimensi.
Jika kita berbicara tentang animasi objek, OSG menyediakan pengembang dengan dua opsi untuk implementasinya:
- Animasi prosedural diimplementasikan secara terprogram melalui transformasi objek dan atributnya
- Mengekspor animasi dari editor 3D dan mengelolanya dari kode aplikasi
Untuk memulai, pertimbangkan kemungkinan pertama, sebagai yang paling jelas. Kami pasti akan membicarakan yang kedua nanti.
1. Animasi morphing prosedural
Saat melintasi grafik adegan, OSG mentransfer data ke pipa OpenGL, yang berjalan di utas terpisah. Utas ini harus disinkronkan dengan utas pemrosesan lainnya di setiap bingkai. Kegagalan untuk melakukannya dapat menyebabkan metode frame () selesai sebelum memproses data geometri. Ini akan menyebabkan perilaku dan crash program tidak dapat diprediksi. OSG menawarkan solusi untuk masalah ini dalam bentuk metode setDataVariance () dari kelas osg :: Object, yang merupakan basis untuk semua objek pemandangan. Anda dapat mengatur tiga mode pemrosesan untuk objek
- UNSPECIFIED (secara default) - OSG secara independen menentukan urutan pemrosesan objek.
- STATIC - objek tidak dapat diubah dan urutan pemrosesan tidak penting. Mempercepat rendering secara signifikan.
- DINAMIK - objek harus diproses sebelum mulai rendering.
Pengaturan ini dapat diatur kapan saja dengan menelepon
node->setDataVariance( osg::Object::DYNAMIC );
Praktik yang diterima secara umum adalah untuk memodifikasi geometri "on the fly", yaitu, mengubah koordinat simpul, normals warna, dan tekstur secara dinamis di setiap frame, memperoleh geometri yang bisa berubah. Teknik ini disebut animasi morphing. Dalam hal ini, urutan pemrosesan geometri sangat menentukan - semua perubahannya harus dihitung ulang sebelum menggambar dimulai. Untuk menggambarkan trik ini, kita sedikit memodifikasi contoh kotak berwarna, memaksa salah satu simpulnya berputar di sekitar sumbu X.
Contoh animquadmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Kami akan membuat kotak dalam fungsi terpisah
osg::Geometry *createQuad() { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(1.0f, 0.0f, 0.0f) ); vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) ); vertices->push_back( osg::Vec3(0.0f, 0.0f, 1.0f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back( osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f) ); colors->push_back( osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f) ); colors->push_back( osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) ); colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); }
deskripsi yang, pada prinsipnya, tidak diperlukan, karena kami telah melakukan tindakan seperti itu berulang kali. Untuk memodifikasi simpul dari persegi ini, kita menulis kelas DynamicQuadCallback, mewarisinya dari osg :: Drawable :: UpdateCallback
class DynamicQuadCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor *, osg::Drawable *drawable); };
mengganti metode pembaruan () di dalamnya
void DynamicQuadCallback::update(osg::NodeVisitor *, osg::Drawable *drawable) { osg::Geometry *quad = static_cast<osg::Geometry *>(drawable); if (!quad) return; osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray()); if (!vertices) return; osg::Quat quat(osg::PI * 0.01, osg::X_AXIS); vertices->back() = quat * vertices->back(); quad->dirtyDisplayList(); quad->dirtyBound(); }
Di sini kita mendapatkan pointer ke objek geometri
osg::Geometry *quad = static_cast<osg::Geometry *>(drawable);
kita membaca dari geometri daftar simpul (atau lebih tepatnya pointer ke sana)
osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray());
Untuk mendapatkan elemen terakhir (titik terakhir) dalam array, kelas osg :: Array menyediakan metode back (). Untuk memutar simpul relatif terhadap sumbu X, kami memperkenalkan angka empat
osg::Quat quat(osg::PI * 0.01, osg::X_AXIS);
yaitu, kami menetapkan angka empat yang mengimplementasikan rotasi di sekitar sumbu X dengan sudut 0,01 * Pi. Putar titik dengan mengalikan angka empat dengan vektor yang menentukan koordinat titik tersebut
vertices->back() = quat * vertices->back();
Dua panggilan terakhir menceritakan daftar tampilan dan pararel dimensionalepiped untuk geometri yang dimodifikasi
quad->dirtyDisplayList(); quad->dirtyBound();
Dalam tubuh fungsi utama (), kita membuat kotak, mengatur mode gambar dinamis untuknya, dan menambahkan panggilan balik memodifikasi geometri
osg::Geometry *quad = createQuad(); quad->setDataVariance(osg::Object::DYNAMIC); quad->setUpdateCallback(new DynamicQuadCallback);
Saya akan meninggalkan sembarang membuat simpul root dan meluncurkan viewer, karena kami telah melakukan ini setidaknya dua puluh kali dengan cara yang berbeda. Hasilnya, kami memiliki animasi morphing yang paling sederhana

Sekarang cobalah untuk menghapus (mengomentari) panggilan setDataVariance (). Mungkin kita tidak akan melihat kejahatan apa pun dalam kasus ini - secara default, OSG mencoba menentukan secara otomatis kapan akan memperbarui data geometri, mencoba menyinkronkan dengan rendering. Kemudian cobalah mengubah mode dari DINAMIK ke STATIC dan akan terlihat bahwa gambar tidak membuat lancar, dengan tersentak terlihat, kesalahan dan peringatan seperti ini mengalir ke konsol
Warning: detected OpenGL error 'invalid value' at after RenderBin::draw(..)
Jika Anda tidak menjalankan metode dirtyDisplayList (), maka OpenGL akan mengabaikan semua perubahan pada geometri dan akan menggunakan daftar tampilan yang dibuat di awal untuk membuat kotak untuk rendering. Hapus panggilan ini dan Anda akan melihat bahwa tidak ada animasi.
Tanpa memanggil metode dirtyBound (), kotak pembatas tidak akan dihitung ulang dan OSG akan memangkas wajah yang tidak terlihat dengan salah.
2. Konsep interpolasi gerak
Misalkan kereta yang berangkat dari stasiun A ke stasiun B membutuhkan waktu 15 menit untuk bepergian. Bagaimana kita bisa mensimulasikan situasi ini dengan mengubah posisi kereta di callback? Cara termudah adalah dengan mengaitkan posisi stasiun A dengan waktu 0, dan stasiun B dengan 15 menit dan memindahkan kereta secara merata di antara waktu-waktu ini. Pendekatan paling sederhana ini disebut interpolasi linier. Dalam interpolasi linier, vektor yang menentukan posisi titik tengah dijelaskan oleh rumus
p = (1 - t) * p0 + t * p1
di mana p0 adalah titik awal; p1 adalah titik akhir; t adalah parameter yang bervariasi seragam dari 0 hingga 1. Namun, pergerakan kereta jauh lebih rumit: ia meninggalkan stasiun A, berakselerasi, kemudian bergerak dengan kecepatan konstan, dan kemudian melambat, berhenti di stasiun B. Proses semacam itu tidak lagi mampu menggambarkan interpolasi linier dan Itu terlihat tidak alami.
OSG menyediakan pengembang dengan perpustakaan osgAnimation, yang berisi sejumlah algoritma interpolasi standar yang digunakan untuk dengan lancar menggerakkan pergerakan objek adegan. Masing-masing fungsi ini biasanya memiliki dua argumen: nilai awal parameter (biasanya 0) dan nilai akhir parameter (biasanya 1). Fungsi-fungsi ini dapat diterapkan pada awal gerakan (InMotion), ke akhir gerakan (OutMotion) atau ke awal dan akhir gerakan (InOutMotion)
Jenis gerakan | di kelas | keluar kelas | di / keluar kelas |
---|
Interpolasi linier | LinearMotion | - | - |
Interpolasi kuadratik | InQuadMotion | OutQuadMotion | InOutQuadMotion |
Interpolasi kubik | InCubicMotion | Promosi luar | InOutCubicMotion |
Interpolasi 4-urutan | InQuartMotion | OutQuartMotion | InOutQuartMotion |
Interpolasi Efek Bouncing | InBounceMotion | OutBounceMotion | InOutBounceMotion |
Interpolasi rebound elastis | InElasticMotion | OutElasticMotion | InOutElasticMotion |
Interpolasi sinusoidal | InSineMotion | Emosi luar biasa | InOutSineMotion |
Interpolasi terbalik | Inbackmotion | Outbackmotion | InOutBackMotion |
Interpolasi melingkar | InCircMotion | Peredaran | InOutCircMotion |
Interpolasi eksponensial | InExpoMotion | Eksploitasi berlebihan | InOutExpoMotion |
Untuk membuat interpolasi linier dari pergerakan suatu objek, kita menulis kode seperti itu
osg::ref_ptr<osgAnimation::LinearMotion> motion = new osgAnimation::LinearMotion(0.0f, 1.0f);
3. Animasi node transformasi
Animasi lintasan adalah jenis animasi yang paling umum dalam aplikasi grafis. Teknik ini dapat digunakan untuk menghidupkan gerakan mobil, penerbangan pesawat, atau gerakan kamera. Lintasan sudah ditentukan sebelumnya, dengan semua posisi, rotasi, dan skala berubah pada titik-titik utama dalam waktu. Ketika siklus simulasi dimulai, keadaan objek dihitung ulang di setiap frame, menggunakan interpolasi linier untuk posisi dan penskalaan dan interpolasi linear bola untuk angka empat rotasi. Untuk melakukan ini, gunakan metode internal slerp () dari kelas osg :: Quat.
OSG menyediakan kelas osg :: AnimationPath untuk menjelaskan jalur yang bervariasi waktu. Metode sisipan kelas ini () digunakan untuk menambahkan titik kontrol yang sesuai dengan titik-titik tertentu dalam waktu ke lintasan. Titik kontrol dijelaskan oleh kelas osg :: AnimationPath :: ControlPoint, konstruktor yang mengambil posisi sebagai parameter, dan, secara opsional, rotasi objek dan parameter penskalaan. Sebagai contoh
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->insert(t1, osg::AnimationPath::ControlPoint(pos1, rot1, scale1)); path->insert(t2, ...);
Di sini t1, t2 adalah contoh waktu dalam detik; rot1 adalah parameter rotasi pada waktu t1, dijelaskan oleh angka empat :: jumlah Quat.
Dimungkinkan untuk mengontrol loop animasi melalui metode setLoopMode (). Secara default, mode LOOP dihidupkan - animasi akan terus diulang. Nilai lain yang mungkin: NO_LOOPING - mainkan animasi sekali dan SWING - putar gerakan ke arah maju dan mundur.
Setelah semua inisialisasi selesai, kami lampirkan objek osg :: AnimationPath ke objek built-in osg :: AnimationPathCallback, yang berasal dari kelas osg :: NodeCallback.
4. Contoh animasi gerakan di sepanjang jalan
Sekarang kita akan membuat cessna kita bergerak dalam lingkaran dengan pusat di titik (0,0,0). Posisi pesawat pada lintasan akan dihitung dengan menginterpolasi posisi dan orientasi antara bingkai kunci secara linear.
Contoh animcessnamain.h #ifndef MAIN_H #define MAIN_H #include <osg/AnimationPath> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Kami mulai dengan membuat lintasan pesawat, mengambil kode ini menjadi fungsi terpisah
osg::AnimationPath *createAnimationPath(double radius, double time) { osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP); unsigned int numSamples = 32; double delta_yaw = 2.0 * osg::PI / (static_cast<double>(numSamples) - 1.0); double delta_time = time / static_cast<double>(numSamples); for (unsigned int i = 0; i < numSamples; ++i) { double yaw = delta_yaw * i; osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0); osg::Quat rot(-yaw, osg::Z_AXIS); path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot)); } return path.release(); }
Sebagai parameter, fungsi tersebut mengambil jari-jari lingkaran di mana pesawat bergerak dan waktu selama itu akan membuat satu revolusi. Di dalam fungsinya, buat objek lintasan dan nyalakan mode perulangan animasi
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP);
Kode berikut
unsigned int numSamples = 32; double delta_yaw = 2.0 * osg::PI / (static_cast<double>(numSamples) - 1.0); double delta_time = time / static_cast<double>(numSamples);
menghitung parameter perkiraan lintasan. Kami membagi seluruh lintasan menjadi numContoh bagian lurus, dan menghitung perubahan sudut rotasi pesawat di sekitar sumbu vertikal (yaw) delta_yaw dan perubahan waktu delta_time ketika bergerak dari bagian ke bagian. Sekarang buat titik kontrol yang diperlukan
for (unsigned int i = 0; i < numSamples; ++i) { double yaw = delta_yaw * i; osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0); osg::Quat rot(-yaw, osg::Z_AXIS); path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot)); }
Dalam siklus, semua bagian lintasan dari yang pertama ke yang terakhir diurutkan. Setiap titik kontrol ditandai dengan sudut yaw
double yaw = delta_yaw * i;
posisi pusat massa pesawat di ruang angkasa
osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0);
Rotasi pesawat ke sudut yaw yang diinginkan (relatif terhadap sumbu vertikal) diatur oleh angka empat
osg::Quat rot(-yaw, osg::Z_AXIS);
dan kemudian menambahkan parameter yang dihitung ke daftar titik kontrol jalan
path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot));
Dalam program utama, kami memperhatikan nuansa dalam menunjukkan nama file model pesawat saat boot
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg.0,0,90.rot");
- akhiran ".0,0,90.rot" telah ditambahkan ke nama file. Mekanisme untuk memuat geometri dari file yang digunakan dalam OSG memungkinkan Anda menentukan posisi awal dan orientasi model setelah memuat. Dalam hal ini, kami ingin model diputar 90 derajat di sekitar sumbu Z setelah pemuatan.
Selanjutnya, simpul akar dibuat, yang merupakan simpul transformasi, dan objek model ditambahkan padanya sebagai simpul anak
osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform; root->addChild(model.get());
Sekarang buat callback animasi lintasan, tambahkan jalur yang dibuat oleh fungsi createAnimationPath () ke dalamnya
osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback; apcb->setAnimationPath(createAnimationPath(50.0, 6.0));
Lampirkan panggilan balik ini ke node transformasi
root->setUpdateCallback(apcb.get());
Penampil diinisialisasi dan diluncurkan seperti biasa.
osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
Dapatkan animasi gerakan pesawat

Pikirkan Anda menemukan sesuatu yang aneh dalam contoh ini? Sebelumnya, misalnya, dalam sebuah program saat merender ke suatu tekstur, Anda secara eksplisit mengubah matriks transformasi untuk mencapai perubahan posisi model dalam ruang. Di sini kita hanya membuat simpul transformasi dan dalam kode tidak ada tugas matriks eksplisit di mana pun.
Rahasianya adalah kelas khusus osg :: AnimationPathCallback melakukan pekerjaan ini. Sesuai dengan posisi objek saat ini di jalan, ia menghitung matriks transformasi dan secara otomatis menerapkannya pada node transformasi yang dilampirkan, menyelamatkan pengembang dari banyak operasi rutin.
Perlu dicatat bahwa melampirkan osg :: AnimationPathCallback ke jenis node lainnya tidak hanya tidak akan berpengaruh, tetapi juga dapat menyebabkan perilaku program yang tidak ditentukan. Penting untuk diingat bahwa panggilan balik ini hanya memengaruhi node transformasi.
5. Animasi kontrol perangkat lunak
Kelas osg :: AnimationPathCallback menyediakan metode untuk mengontrol animasi selama eksekusi program.
- reset () - reset animasi dan mainkan terlebih dahulu.
- setPause () - menjeda animasi. Mengambil nilai boolean sebagai parameter
- setTimeOffset () - mengatur offset waktu sebelum dimulainya animasi.
- setTimeMultiplier () - mengatur faktor waktu untuk akselerasi / perlambatan animasi.
Misalnya, untuk menghapus animasi dari jeda dan mengatur ulang, kami mengeksekusi kode tersebut
apcb->setPause(false); apcb->reset();
dan untuk memulai animasi dari detik keempat setelah memulai program dengan akselerasi ganda, kode seperti itu
apcb->setTimeOffset(4.0f); apcb->setTimeMultiplier(2.0f);
6. Urutan rendering primitif di OpenGL
OpenGL menyimpan data titik dan primitif dalam berbagai buffer, seperti buffer warna, buffer kedalaman, buffer stensil, dan sebagainya. Selain itu, ia tidak menimpa simpul dan wajah segitiga yang sudah dikirim ke pipanya. Ini berarti bahwa OpenGL membuat geometri baru, terlepas dari bagaimana geometri yang ada dibuat. Ini berarti bahwa urutan primitif dikirim ke jalur render secara signifikan mempengaruhi hasil akhir yang kita lihat di layar.
Berdasarkan data buffer kedalaman, OpenGL akan menggambar objek buram dengan benar, menyortir piksel berdasarkan jaraknya dari pengamat. Namun, ketika menggunakan teknik pencampuran warna, misalnya, ketika mengimplementasikan objek transparan dan tembus cahaya, operasi khusus akan dilakukan untuk memperbarui buffer warna. Pixel baru dan lama dari gambar dicampur, dengan mempertimbangkan nilai saluran alpha (komponen warna keempat). Ini mengarah pada fakta bahwa urutan rendering dari tepi transparan dan buram mempengaruhi hasil akhir

Dalam gambar, dalam situasi di sebelah kiri, pada objek buram pertama dan kemudian transparan dikirim ke pipa, yang menyebabkan pergeseran yang benar dalam buffer warna dan tampilan wajah yang benar. Dalam situasi yang tepat, objek transparan pertama digambar, dan kemudian buram, yang menyebabkan tampilan yang salah.
Metode setRenderingHint () dari kelas osg :: StateSet menunjukkan kepada OSG urutan render node dan objek geometri yang diperlukan, jika ini perlu dilakukan secara eksplisit. Metode ini hanya menunjukkan apakah wajah transparan harus diperhitungkan atau tidak harus diperhitungkan saat rendering, dengan demikian memastikan bahwa jika ada wajah transparan dalam adegan, buram dan kemudian wajah transparan akan ditarik terlebih dahulu, dengan mempertimbangkan jarak dari pengamat. Untuk memberi tahu mesin bahwa simpul ini buram, kami menggunakan kode ini
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN);
atau mengandung tepi transparan
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
7. Contoh implementasi objek yang tembus cahaya
Mari kita coba mengilustrasikan semua pengantar teoretis di atas dengan contoh nyata implementasi objek yang tembus cahaya.
Contoh transparansimain.h #ifndef MAIN_H #define MAIN_H #include <osg/BlendFunc> #include <osg/Texture2D> #include <osg/Geometry> #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::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(-0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, 0.5f) ); vertices->push_back( osg::Vec3(-0.5f, 0.0f, 0.5f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); osg::ref_ptr<osg::Geode> geode = new osg::Geode; geode->addDrawable(quad.get()); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/Images/lz.rgb"); texture->setImage(image.get()); osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); osg::StateSet *stateset = geode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture.get()); stateset->setAttributeAndModes(blendFunc); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(geode.get()); root->addChild(osgDB::readNodeFile("../data/glider.osg")); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Sebagian besar, kode yang ditampilkan di sini tidak mengandung sesuatu yang baru: dua objek geometrik dibuat - kotak bertekstur dan hang glider, model yang diambil dari file. Namun, kami menerapkan warna transparan putih ke semua simpul persegi
colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) );
- nilai kanal alfa adalah 0,5, yang bila dicampur dengan warna tekstur, akan memberikan efek objek yang tembus cahaya. Selain itu, fungsi pencampuran warna harus diatur untuk pemrosesan transparansi.
osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
meneruskannya ke mesin status OpenGL
stateset->setAttributeAndModes(blendFunc);
Ketika menyusun dan menjalankan program ini, kami mendapatkan hasil berikut

Hentikan itu! Dan di mana transparansi? Masalahnya adalah kita lupa memberi tahu mesin bahwa tepian transparan harus diproses, yang mudah diselesaikan dengan menelepon
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
setelah itu kita mendapatkan hasil yang kita butuhkan - sayap peluncur layang bersinar melalui kotak bertekstur transparan

Parameter dari fungsi pencampuran GL_SRC_ALPHA dan GL_ONE_MINUS_SRC_ALPHA berarti bahwa piksel layar yang dihasilkan saat menggambar wajah transparan akan memiliki komponen warna yang dihitung oleh rumus.
R = srcR * srcA + dstR * (1 - srcA) G = srcG * srcA + dstG * (1 - srcA) B = srcB * srcA + dstB * (1 - srcA)
di mana [srcR, srcG, srcB] adalah komponen warna dari tekstur kuadrat;
[dstR, dstG, dstB] - komponen warna dari setiap piksel area di mana wajah yang transparan ditumpangkan, mengingat latar belakang dan tepi buram sayap glider sudah digambar di tempat ini. Maksud saya srcA adalah komponen alfa dari warna kotak.Metode seRenderingHint () dengan sempurna mengatur rendering primitif, tetapi menggunakannya tidak sangat efisien, karena menyortir objek transparan dengan kedalaman ketika rendering frame adalah operasi yang sangat intensif sumber daya. Oleh karena itu, pengembang harus mengurus urutan menggambar wajah mereka sendiri, jika mungkin pada tahap awal persiapan adegan.8. Animasi atribut negara
Menggunakan animasi, Anda juga dapat mengontrol atribut negara. Seluruh efek visual dapat dihasilkan dengan mengubah properti dari satu atau lebih atribut render. Animasi semacam ini yang mengubah keadaan atribut render mudah diterapkan melalui mekanisme panggilan balik saat memperbarui adegan.Kelas interpolasi standar juga dapat digunakan untuk menentukan fungsi mengubah parameter atribut.Kami sudah memiliki pengalaman dalam membuat objek transparan. Kita tahu bahwa jika komponen alfa warna adalah nol, kita mendapatkan objek yang sepenuhnya transparan, dengan nilai 1 - benar-benar buram. Jelas bahwa dengan memvariasikan parameter ini dari 0 hingga 1 dalam waktu, efek dari penampilan bertahap atau hilangnya suatu objek dapat diperoleh. Kami menggambarkan ini dengan contoh nyata.Misalnya memudarmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geode> #include <osg/Geometry> #include <osg/BlendFunc> #include <osg/Material> #include <osgAnimation/EaseMotion> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Kami mulai dengan membuat penangan panggilan balik untuk mengubah nilai saluran alpha dari waktu ke waktu class AlphaFadingCallback : public osg::StateAttributeCallback { public: AlphaFadingCallback() { _motion = new osgAnimation::InOutCubicMotion(0.0f, 1.0f); } virtual void operator() (osg::StateAttribute* , osg::NodeVisitor*); protected: osg::ref_ptr<osgAnimation::InOutCubicMotion> _motion; };
Parameter _motion yang dilindungi akan menentukan fungsi dengan mana nilai alpha akan berubah seiring waktu. Untuk contoh ini, kita memilih pendekatan spline kubik, mengaturnya segera, di konstruktor kelas AlphaFadingCallback() { _motion = new osgAnimation::InOutCubicMotion(0.0f, 1.0f); }
Ketergantungan ini dapat diilustrasikan oleh kurva seperti itu.
Dalam konstruktor objek InOutCubicMotion, kita menentukan batas nilai yang diperkirakan dari 0 hingga 1. Selanjutnya, kita mendefinisikan kembali operator () untuk kelas ini dengan cara ini void AlphaFadingCallback::operator()(osg::StateAttribute *sa, osg::NodeVisitor *nv) { (void) nv; osg::Material *material = static_cast<osg::Material *>(sa); if (material) { _motion->update(0.0005f); float alpha = _motion->getValue(); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, alpha)); } }
Dapatkan pointer ke materi osg::Material *material = static_cast<osg::Material *>(sa);
Nilai abstrak atribut datang ke callback, namun kami akan melampirkan handler ini ke materi, oleh karena itu adalah pointer ke material yang akan datang, oleh karena itu kami dapat dengan aman mengkonversi atribut state ke pointer ke material. Selanjutnya, kita mengatur interval pembaruan dari fungsi yang diperkirakan - semakin besar, semakin cepat parameter akan berubah dalam rentang yang ditentukan _motion->update(0.0005f);
Kami membaca nilai fungsi aproksimasi float alpha = _motion->getValue();
dan memberikan materi nilai warna difus baru material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, alpha));
Sekarang mari kita membentuk adegan dalam fungsi utama (). Saya pikir Anda lelah setiap kali membangun persegi pada simpul, jadi kami menyederhanakan tugas - kami menghasilkan poligon persegi dengan fungsi OSG standar osg::ref_ptr<osg::Drawable> quad = osg::createTexturedQuadGeometry( osg::Vec3(-0.5f, 0.0f, -0.5f), osg::Vec3(1.0f, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f));
Parameter pertama adalah titik dari mana sudut kiri bawah kotak akan dibangun, dua parameter lainnya menentukan koordinat diagonal. Setelah menemukan kotak, kami membuat bahan untuk itu osg::ref_ptr<osg::Material> material = new osg::Material; material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, 0.5f));
Kami menunjukkan opsi warna material. Warna ambient adalah parameter yang mencirikan warna material di area yang diarsir, tidak dapat diakses oleh sumber warna. Warna difus adalah warna bahan itu sendiri, yang mencirikan kemampuan permukaan untuk meredakan warna yang jatuh di atasnya, yaitu apa yang biasa kita sebut warna dalam kehidupan sehari-hari. Parameter FRONT_AND_BACK menunjukkan bahwa atribut warna ini ditetapkan untuk sisi depan dan belakang wajah geometri.Tetapkan materi untuk penangan yang dibuat sebelumnya. material->setUpdateCallback(new AlphaFadingCallback);
Tetapkan materi yang dibuat ke kotak geode->getOrCreateStateSet()->setAttributeAndModes(material.get());
dan mengatur atribut lainnya - fungsi pencampuran warna dan menunjukkan bahwa objek ini memiliki tepi transparan geode->getOrCreateStateSet()->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); geode->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
Kami menyelesaikan pembentukan adegan dan menjalankan pemirsa osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(geode.get()); root->addChild(osgDB::readNodeFile("../data/glider.osg")); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
Kami mendapatkan hasilnya dalam bentuk kotak yang muncul dengan mulus di tempat kejadian
Alih-alih kesimpulan: komentar kecil tentang dependensi
Tentunya contoh Anda tidak dikompilasi, memberikan kesalahan pada tahap pembuatan. Ini bukan kebetulan - perhatikan baris di file header main.h #include <osgAnimation/EaseMotion>
Direktori header OSG dari mana file header diambil biasanya menunjuk ke perpustakaan yang berisi implementasi fungsi dan kelas yang dijelaskan dalam header. Oleh karena itu, tampilan osgAnimation / direktori harus menyarankan bahwa perpustakaan dengan nama yang sama harus ditambahkan ke daftar tautan skrip build proyek, kira-kira seperti ini (dengan mempertimbangkan jalur ke perpustakaan dan versi build) LIBS += -losgAnimation
Dilanjutkan ...